Convert module funcs to ToString/Parse
This commit is contained in:
		
							parent
							
								
									5240b78487
								
							
						
					
					
						commit
						facc294d66
					
				| @ -36,9 +36,9 @@ module private Helpers = | ||||
|             IsPublic            = row.bool     "is_public" | ||||
|             PageSize            = row.int      "page_size" | ||||
|             TimeZoneId          = TimeZoneId    (row.string "time_zone_id") | ||||
|             RequestSort         = RequestSort.fromCode     (row.string "request_sort") | ||||
|             DefaultEmailType    = EmailFormat.fromCode     (row.string "default_email_type") | ||||
|             AsOfDateDisplay     = AsOfDateDisplay.fromCode (row.string "as_of_date_display") | ||||
|             RequestSort         = RequestSort.Parse     (row.string "request_sort") | ||||
|             DefaultEmailType    = EmailFormat.Parse     (row.string "default_email_type") | ||||
|             AsOfDateDisplay     = AsOfDateDisplay.Parse (row.string "as_of_date_display") | ||||
|         } | ||||
|      | ||||
|     /// Map a row to a Member instance | ||||
| @ -47,7 +47,7 @@ module private Helpers = | ||||
|             SmallGroupId = SmallGroupId     (row.uuid "small_group_id") | ||||
|             Name         = row.string       "member_name" | ||||
|             Email        = row.string       "email" | ||||
|             Format       = row.stringOrNone "email_format" |> Option.map EmailFormat.fromCode | ||||
|             Format       = row.stringOrNone "email_format" |> Option.map EmailFormat.Parse | ||||
|         } | ||||
|      | ||||
|     /// Map a row to a Prayer Request instance | ||||
| @ -60,8 +60,8 @@ module private Helpers = | ||||
|             Requestor      = row.stringOrNone        "requestor" | ||||
|             Text           = row.string              "request_text" | ||||
|             NotifyChaplain = row.bool                "notify_chaplain" | ||||
|             RequestType    = PrayerRequestType.fromCode (row.string "request_type") | ||||
|             Expiration     = Expiration.fromCode        (row.string "expiration") | ||||
|             RequestType    = PrayerRequestType.Parse (row.string "request_type") | ||||
|             Expiration     = Expiration.Parse        (row.string "expiration") | ||||
|         } | ||||
|      | ||||
|     /// Map a row to a Small Group instance | ||||
| @ -185,7 +185,7 @@ module Members = | ||||
|                 "@groupId", Sql.uuid         mbr.SmallGroupId.Value | ||||
|                 "@name",    Sql.string       mbr.Name | ||||
|                 "@email",   Sql.string       mbr.Email | ||||
|                 "@format",  Sql.stringOrNone (mbr.Format |> Option.map EmailFormat.toCode) ] | ||||
|                 "@format",  Sql.stringOrNone (mbr.Format |> Option.map string) ] | ||||
|      | ||||
|     /// Retrieve a small group member by its ID | ||||
|     let tryById (memberId : MemberId) = | ||||
| @ -257,10 +257,10 @@ module PrayerRequests = | ||||
|                          OR request_type = @expecting) | ||||
|                     AND expiration <> @forced", | ||||
|                 [   "@asOf",      Sql.parameter asOf | ||||
|                     "@manual",    Sql.string    (Expiration.toCode Manual) | ||||
|                     "@longTerm",  Sql.string    (PrayerRequestType.toCode LongTermRequest) | ||||
|                     "@expecting", Sql.string    (PrayerRequestType.toCode Expecting) | ||||
|                     "@forced",    Sql.string    (Expiration.toCode Forced) ] | ||||
|                     "@manual",    Sql.string    (string Manual) | ||||
|                     "@longTerm",  Sql.string    (string LongTermRequest) | ||||
|                     "@expecting", Sql.string    (string Expecting) | ||||
|                     "@forced",    Sql.string    (string Forced) ] | ||||
|             else "", [] | ||||
|         Custom.list | ||||
|             $"SELECT * | ||||
| @ -287,15 +287,15 @@ module PrayerRequests = | ||||
|                 notify_chaplain = EXCLUDED.notify_chaplain, | ||||
|                 expiration      = EXCLUDED.expiration" | ||||
|             [   "@id",             Sql.uuid         req.Id.Value | ||||
|                 "@type",           Sql.string       (PrayerRequestType.toCode req.RequestType) | ||||
|                 "@type",           Sql.string       (string req.RequestType) | ||||
|                 "@userId",         Sql.uuid         req.UserId.Value | ||||
|                 "@groupId",        Sql.uuid         req.SmallGroupId.Value | ||||
|                 "@entered",        Sql.parameter    (NpgsqlParameter ("@entered", req.EnteredDate)) | ||||
|                 "@updated",        Sql.parameter    (NpgsqlParameter ("@updated", req.UpdatedDate)) | ||||
|                 "@entered",        Sql.parameter    (NpgsqlParameter("@entered", req.EnteredDate)) | ||||
|                 "@updated",        Sql.parameter    (NpgsqlParameter("@updated", req.UpdatedDate)) | ||||
|                 "@requestor",      Sql.stringOrNone req.Requestor | ||||
|                 "@text",           Sql.string       req.Text | ||||
|                 "@notifyChaplain", Sql.bool         req.NotifyChaplain | ||||
|                 "@expiration",     Sql.string       (Expiration.toCode req.Expiration) ] | ||||
|                 "@expiration",     Sql.string       (string req.Expiration) ] | ||||
|      | ||||
|     /// Search prayer requests for the given term | ||||
|     let searchForGroup group searchTerm pageNbr = | ||||
| @ -320,9 +320,8 @@ module PrayerRequests = | ||||
|                 [ "@updated", Sql.parameter (NpgsqlParameter ("@updated", req.UpdatedDate)) ] | ||||
|             else "", [] | ||||
|         Custom.nonQuery $"UPDATE pt.prayer_request SET expiration = @expiration{sql} WHERE id = @id" | ||||
|                         ([  "@expiration", Sql.string (Expiration.toCode req.Expiration) | ||||
|                             "@id",         Sql.uuid   req.Id.Value ] | ||||
|                         |> List.append parameters) | ||||
|                         ([ "@expiration", Sql.string (string req.Expiration); "@id", Sql.uuid req.Id.Value ] | ||||
|                          |> List.append parameters) | ||||
| 
 | ||||
| 
 | ||||
| /// Functions to retrieve small group information | ||||
| @ -455,13 +454,13 @@ module SmallGroups = | ||||
|                 "@lineColor",           Sql.string pref.LineColor | ||||
|                 "@headingFontSize",     Sql.int    pref.HeadingFontSize | ||||
|                 "@textFontSize",        Sql.int    pref.TextFontSize | ||||
|                 "@requestSort",         Sql.string (RequestSort.toCode pref.RequestSort) | ||||
|                 "@requestSort",         Sql.string (string pref.RequestSort) | ||||
|                 "@groupPassword",       Sql.string pref.GroupPassword | ||||
|                 "@defaultEmailType",    Sql.string (EmailFormat.toCode pref.DefaultEmailType) | ||||
|                 "@defaultEmailType",    Sql.string (string pref.DefaultEmailType) | ||||
|                 "@isPublic",            Sql.bool   pref.IsPublic | ||||
|                 "@timeZoneId",          Sql.string (TimeZoneId.toString pref.TimeZoneId) | ||||
|                 "@pageSize",            Sql.int    pref.PageSize | ||||
|                 "@asOfDateDisplay",     Sql.string (AsOfDateDisplay.toCode pref.AsOfDateDisplay) ] | ||||
|                 "@asOfDateDisplay",     Sql.string (string pref.AsOfDateDisplay) ] | ||||
|      | ||||
|     /// Get a small group by its ID | ||||
|     let tryById (groupId : SmallGroupId) = | ||||
|  | ||||
| @ -11,19 +11,20 @@ type AsOfDateDisplay = | ||||
|     /// The as-of date should be displayed in the culture's long date format | ||||
|     | LongDate | ||||
| 
 | ||||
| /// Functions to support as-of date display options | ||||
| module AsOfDateDisplay = | ||||
|      | ||||
|     /// Convert to a DU case from a single-character string | ||||
|     let fromCode code = | ||||
|     /// Convert this to a single-character code | ||||
|     override this.ToString() = | ||||
|         match this with | ||||
|         | NoDisplay -> "N" | ||||
|         | ShortDate -> "S" | ||||
|         | LongDate -> "L" | ||||
| 
 | ||||
|     /// <summary>Create an <c>AsOfDateDisplay</c> from a single-character code</summary> | ||||
|     static member Parse code = | ||||
|         match code with | ||||
|         | "N" -> NoDisplay | ||||
|         | "S" -> ShortDate | ||||
|         | "L" -> LongDate | ||||
|         | _   -> invalidArg "code" $"Unknown code {code}" | ||||
|      | ||||
|     /// Convert this DU case to a single-character string | ||||
|     let toCode = function NoDisplay -> "N" | ShortDate -> "S" | LongDate -> "L" | ||||
|         | _ -> invalidArg "code" $"Unknown code {code}" | ||||
| 
 | ||||
| 
 | ||||
| /// Acceptable e-mail formats | ||||
| @ -33,18 +34,18 @@ type EmailFormat = | ||||
|     /// Plain-text e-mail | ||||
|     | PlainTextFormat | ||||
| 
 | ||||
| /// Functions to support e-mail formats | ||||
| module EmailFormat = | ||||
|      | ||||
|     /// Convert to a DU case from a single-character string | ||||
|     let fromCode code = | ||||
|     /// Convert this to a single-character code | ||||
|     override this.ToString() = | ||||
|         match this with | ||||
|         | HtmlFormat -> "H" | ||||
|         | PlainTextFormat -> "P" | ||||
| 
 | ||||
|     /// <summary>Create an <c>EmailFormat</c> from a single-character code</summary> | ||||
|     static member Parse code = | ||||
|         match code with | ||||
|         | "H" -> HtmlFormat | ||||
|         | "P" -> PlainTextFormat | ||||
|         | _   -> invalidArg "code" $"Unknown code {code}" | ||||
|      | ||||
|     /// Convert this DU case to a single-character string | ||||
|     let toCode = function HtmlFormat -> "H" | PlainTextFormat -> "P" | ||||
|         | _ -> invalidArg "code" $"Unknown code {code}" | ||||
| 
 | ||||
| 
 | ||||
| /// Expiration for requests | ||||
| @ -56,19 +57,20 @@ type Expiration = | ||||
|     /// Force immediate expiration | ||||
|     | Forced | ||||
| 
 | ||||
| /// Functions to support expirations | ||||
| module Expiration = | ||||
|      | ||||
|     /// Convert to a DU case from a single-character string | ||||
|     let fromCode code = | ||||
|     /// Convert this to a single-character code | ||||
|     override this.ToString() = | ||||
|         match this with | ||||
|         | Automatic -> "A" | ||||
|         | Manual -> "M" | ||||
|         | Forced -> "F" | ||||
| 
 | ||||
|     /// <summary>Create an <c>Expiration</c> from a single-character code</summary> | ||||
|     static member Parse code = | ||||
|         match code with | ||||
|         | "A" -> Automatic | ||||
|         | "M" -> Manual | ||||
|         | "F" -> Forced | ||||
|         | _   -> invalidArg "code" $"Unknown code {code}" | ||||
|      | ||||
|     /// Convert this DU case to a single-character string | ||||
|     let toCode = function Automatic -> "A" | Manual -> "M" | Forced -> "F" | ||||
|         | _ -> invalidArg "code" $"Unknown code {code}" | ||||
| 
 | ||||
| 
 | ||||
| /// Types of prayer requests | ||||
| @ -84,27 +86,24 @@ type PrayerRequestType = | ||||
|     /// Announcements | ||||
|     | Announcement | ||||
| 
 | ||||
| /// Functions to support prayer request types | ||||
| module PrayerRequestType = | ||||
|      | ||||
|     /// Convert to a DU case from a single-character string | ||||
|     let fromCode code = | ||||
|     /// Convert this to a single-character code | ||||
|     override this.ToString() = | ||||
|         match this with | ||||
|         | CurrentRequest -> "C" | ||||
|         | LongTermRequest -> "L" | ||||
|         | Expecting -> "E" | ||||
|         | PraiseReport -> "P" | ||||
|         | Announcement -> "A" | ||||
| 
 | ||||
|     /// <summary>Create a <c>PrayerRequestType</c> from a single-character code</summary> | ||||
|     static member Parse code = | ||||
|         match code with | ||||
|         | "C" -> CurrentRequest | ||||
|         | "L" -> LongTermRequest | ||||
|         | "E" -> Expecting | ||||
|         | "P" -> PraiseReport | ||||
|         | "A" -> Announcement | ||||
|         | _   -> invalidArg "code" $"Unknown code {code}" | ||||
|      | ||||
|     /// Convert this DU case to a single-character string | ||||
|     let toCode = | ||||
|         function | ||||
|         | CurrentRequest  -> "C" | ||||
|         | LongTermRequest -> "L" | ||||
|         | Expecting       -> "E" | ||||
|         | PraiseReport    -> "P" | ||||
|         | Announcement    -> "A" | ||||
|         | _ -> invalidArg "code" $"Unknown code {code}" | ||||
| 
 | ||||
| 
 | ||||
| /// How requests should be sorted | ||||
| @ -114,18 +113,18 @@ type RequestSort = | ||||
|     /// Sort by requestor/subject, then by date | ||||
|     | SortByRequestor | ||||
| 
 | ||||
| /// Functions to support request sorts | ||||
| module RequestSort = | ||||
|      | ||||
|     /// Convert to a DU case from a single-character string | ||||
|     let fromCode code = | ||||
|     /// Convert this to a single-character code | ||||
|     override this.ToString() = | ||||
|         match this with | ||||
|         | SortByDate -> "D" | ||||
|         | SortByRequestor -> "R" | ||||
| 
 | ||||
|     /// <summary>Create a <c>RequestSort</c> from a single-character code</summary> | ||||
|     static member Parse code = | ||||
|         match code with | ||||
|         | "D" -> SortByDate | ||||
|         | "R" -> SortByRequestor | ||||
|         | _   -> invalidArg "code" $"Unknown code {code}" | ||||
|      | ||||
|     /// Convert this DU case to a single-character string | ||||
|     let toCode = function SortByDate -> "D" | SortByRequestor -> "R" | ||||
|         | _ -> invalidArg "code" $"Unknown code {code}" | ||||
| 
 | ||||
| 
 | ||||
| open System | ||||
| @ -133,33 +132,45 @@ open System | ||||
| /// PK type for the Church entity | ||||
| type ChurchId = | ||||
|     | ChurchId of Guid | ||||
| with | ||||
| 
 | ||||
|     /// The GUID value of the church ID | ||||
|     member this.Value = this |> function ChurchId guid -> guid | ||||
|     member this.Value = | ||||
|         this | ||||
|         |> function | ||||
|             | ChurchId guid -> guid | ||||
| 
 | ||||
| 
 | ||||
| /// PK type for the Member entity | ||||
| type MemberId = | ||||
|     | MemberId of Guid | ||||
| with | ||||
| 
 | ||||
|     /// The GUID value of the member ID | ||||
|     member this.Value = this |> function MemberId guid -> guid | ||||
|     member this.Value = | ||||
|         this | ||||
|         |> function | ||||
|             | MemberId guid -> guid | ||||
| 
 | ||||
| 
 | ||||
| /// PK type for the PrayerRequest entity | ||||
| type PrayerRequestId = | ||||
|     | PrayerRequestId of Guid | ||||
| with | ||||
| 
 | ||||
|     /// The GUID value of the prayer request ID | ||||
|     member this.Value = this |> function PrayerRequestId guid -> guid | ||||
|     member this.Value = | ||||
|         this | ||||
|         |> function | ||||
|             | PrayerRequestId guid -> guid | ||||
| 
 | ||||
| 
 | ||||
| /// PK type for the SmallGroup entity | ||||
| type SmallGroupId = | ||||
|     | SmallGroupId of Guid | ||||
| with | ||||
| 
 | ||||
|     /// The GUID value of the small group ID | ||||
|     member this.Value = this |> function SmallGroupId guid -> guid | ||||
|     member this.Value = | ||||
|         this | ||||
|         |> function | ||||
|             | SmallGroupId guid -> guid | ||||
| 
 | ||||
| 
 | ||||
| /// PK type for the TimeZone entity | ||||
| @ -167,51 +178,58 @@ type TimeZoneId = TimeZoneId of string | ||||
| 
 | ||||
| /// Functions to support time zone IDs | ||||
| module TimeZoneId = | ||||
|      | ||||
| 
 | ||||
|     /// Convert a time zone ID to its string value | ||||
|     let toString = function TimeZoneId it -> it | ||||
|     let toString = | ||||
|         function | ||||
|         | TimeZoneId it -> it | ||||
| 
 | ||||
| 
 | ||||
| /// PK type for the User entity | ||||
| type UserId = | ||||
|     | UserId of Guid | ||||
| with | ||||
| 
 | ||||
|     /// The GUID value of the user ID | ||||
|     member this.Value = this |> function UserId guid -> guid | ||||
|     member this.Value = | ||||
|         this | ||||
|         |> function | ||||
|             | UserId guid -> guid | ||||
| 
 | ||||
| (*-- SPECIFIC VIEW TYPES --*) | ||||
| 
 | ||||
| /// Statistics for churches | ||||
| [<NoComparison; NoEquality>] | ||||
| type ChurchStats = | ||||
|     {   /// The number of small groups in the church | ||||
|         SmallGroups : int | ||||
|          | ||||
|     { | ||||
|         /// The number of small groups in the church | ||||
|         SmallGroups: int | ||||
| 
 | ||||
|         /// The number of prayer requests in the church | ||||
|         PrayerRequests : int | ||||
|          | ||||
|         PrayerRequests: int | ||||
| 
 | ||||
|         /// The number of users who can access small groups in the church | ||||
|         Users : int | ||||
|         Users: int | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| /// Information needed to display the public/protected request list and small group maintenance pages | ||||
| [<NoComparison; NoEquality>] | ||||
| type SmallGroupInfo = | ||||
|     {   /// The ID of the small group | ||||
|         Id : string | ||||
|          | ||||
|     { | ||||
|         /// The ID of the small group | ||||
|         Id: string | ||||
| 
 | ||||
|         /// The name of the small group | ||||
|         Name : string | ||||
|          | ||||
|         Name: string | ||||
| 
 | ||||
|         /// The name of the church to which the small group belongs | ||||
|         ChurchName : string | ||||
|          | ||||
|         ChurchName: string | ||||
| 
 | ||||
|         /// The ID of the time zone for the small group | ||||
|         TimeZoneId : TimeZoneId | ||||
|          | ||||
|         TimeZoneId: TimeZoneId | ||||
| 
 | ||||
|         /// Whether the small group has a publicly-available request list | ||||
|         IsPublic : bool | ||||
|         IsPublic: bool | ||||
|     } | ||||
| 
 | ||||
| (*-- ENTITIES --*) | ||||
| @ -221,195 +239,196 @@ open NodaTime | ||||
| /// This represents a church | ||||
| [<NoComparison; NoEquality>] | ||||
| type Church = | ||||
|     {   /// The ID of this church | ||||
|         Id : ChurchId | ||||
|          | ||||
|     { | ||||
|         /// The ID of this church | ||||
|         Id: ChurchId | ||||
| 
 | ||||
|         /// The name of the church | ||||
|         Name : string | ||||
|          | ||||
|         Name: string | ||||
| 
 | ||||
|         /// The city where the church is | ||||
|         City : string | ||||
|          | ||||
|         City: string | ||||
| 
 | ||||
|         /// The 2-letter state or province code for the church's location | ||||
|         State : string | ||||
|          | ||||
|         State: string | ||||
| 
 | ||||
|         /// Does this church have an active interface with Virtual Prayer Space? | ||||
|         HasVpsInterface : bool | ||||
|          | ||||
|         HasVpsInterface: bool | ||||
| 
 | ||||
|         /// The address for the interface | ||||
|         InterfaceAddress : string option | ||||
|         InterfaceAddress: string option | ||||
|     } | ||||
| 
 | ||||
| /// Functions to support churches | ||||
| module Church = | ||||
|      | ||||
| 
 | ||||
|     /// An empty church | ||||
|     // aww... how sad :( | ||||
|     let empty = | ||||
|         {   Id               = ChurchId Guid.Empty | ||||
|             Name             = "" | ||||
|             City             = "" | ||||
|             State            = "" | ||||
|             HasVpsInterface  = false | ||||
|             InterfaceAddress = None | ||||
|         } | ||||
|      | ||||
|         { Id = ChurchId Guid.Empty | ||||
|           Name = "" | ||||
|           City = "" | ||||
|           State = "" | ||||
|           HasVpsInterface = false | ||||
|           InterfaceAddress = None } | ||||
| 
 | ||||
| 
 | ||||
| /// Preferences for the form and format of the prayer request list | ||||
| [<NoComparison; NoEquality>] | ||||
| type ListPreferences = | ||||
|     {   /// The Id of the small group to which these preferences belong | ||||
|         SmallGroupId : SmallGroupId | ||||
|          | ||||
|     { | ||||
|         /// The Id of the small group to which these preferences belong | ||||
|         SmallGroupId: SmallGroupId | ||||
| 
 | ||||
|         /// The days after which regular requests expire | ||||
|         DaysToExpire : int | ||||
|          | ||||
|         DaysToExpire: int | ||||
| 
 | ||||
|         /// The number of days a new or updated request is considered new | ||||
|         DaysToKeepNew : int | ||||
|          | ||||
|         DaysToKeepNew: int | ||||
| 
 | ||||
|         /// The number of weeks after which long-term requests are flagged for follow-up | ||||
|         LongTermUpdateWeeks : int | ||||
|          | ||||
|         LongTermUpdateWeeks: int | ||||
| 
 | ||||
|         /// The name from which e-mails are sent | ||||
|         EmailFromName : string | ||||
|          | ||||
|         EmailFromName: string | ||||
| 
 | ||||
|         /// The e-mail address from which e-mails are sent | ||||
|         EmailFromAddress : string | ||||
|          | ||||
|         EmailFromAddress: string | ||||
| 
 | ||||
|         /// The fonts to use in generating the list of prayer requests | ||||
|         Fonts : string | ||||
|          | ||||
|         Fonts: string | ||||
| 
 | ||||
|         /// The color for the prayer request list headings | ||||
|         HeadingColor : string | ||||
|          | ||||
|         HeadingColor: string | ||||
| 
 | ||||
|         /// The color for the lines offsetting the prayer request list headings | ||||
|         LineColor : string | ||||
|          | ||||
|         LineColor: string | ||||
| 
 | ||||
|         /// The font size for the headings on the prayer request list | ||||
|         HeadingFontSize : int | ||||
|          | ||||
|         HeadingFontSize: int | ||||
| 
 | ||||
|         /// The font size for the text on the prayer request list | ||||
|         TextFontSize : int | ||||
|          | ||||
|         TextFontSize: int | ||||
| 
 | ||||
|         /// The order in which the prayer requests are sorted | ||||
|         RequestSort : RequestSort | ||||
|          | ||||
|         RequestSort: RequestSort | ||||
| 
 | ||||
|         /// The password used for "small group login" (view-only request list) | ||||
|         GroupPassword : string | ||||
|          | ||||
|         GroupPassword: string | ||||
| 
 | ||||
|         /// The default e-mail type for this class | ||||
|         DefaultEmailType : EmailFormat | ||||
|          | ||||
|         DefaultEmailType: EmailFormat | ||||
| 
 | ||||
|         /// Whether this class makes its request list public | ||||
|         IsPublic : bool | ||||
|          | ||||
|         IsPublic: bool | ||||
| 
 | ||||
|         /// The time zone which this class uses (use tzdata names) | ||||
|         TimeZoneId : TimeZoneId | ||||
|          | ||||
|         TimeZoneId: TimeZoneId | ||||
| 
 | ||||
|         /// The number of requests displayed per page | ||||
|         PageSize : int | ||||
|          | ||||
|         PageSize: int | ||||
| 
 | ||||
|         /// How the as-of date should be automatically displayed | ||||
|         AsOfDateDisplay : AsOfDateDisplay | ||||
|         AsOfDateDisplay: AsOfDateDisplay | ||||
|     } | ||||
| with | ||||
|      | ||||
| 
 | ||||
|     /// The list of fonts to use when displaying request lists (converts "native" to native font stack) | ||||
|     member this.FontStack = | ||||
|         if this.Fonts = "native" then | ||||
|             """system-ui,-apple-system,"Segoe UI",Roboto,Ubuntu,"Liberation Sans",Cantarell,"Helvetica Neue",sans-serif""" | ||||
|         else this.Fonts | ||||
|         else | ||||
|             this.Fonts | ||||
| 
 | ||||
| /// Functions to support list preferences | ||||
| module ListPreferences = | ||||
|      | ||||
| 
 | ||||
|     /// A set of preferences with their default values | ||||
|     let empty = | ||||
|         {   SmallGroupId        = SmallGroupId Guid.Empty | ||||
|             DaysToExpire        = 14 | ||||
|             DaysToKeepNew       = 7 | ||||
|             LongTermUpdateWeeks = 4 | ||||
|             EmailFromName       = "PrayerTracker" | ||||
|             EmailFromAddress    = "prayer@bitbadger.solutions" | ||||
|             Fonts               = "native" | ||||
|             HeadingColor        = "maroon" | ||||
|             LineColor           = "navy" | ||||
|             HeadingFontSize     = 16 | ||||
|             TextFontSize        = 12 | ||||
|             RequestSort         = SortByDate | ||||
|             GroupPassword       = "" | ||||
|             DefaultEmailType    = HtmlFormat | ||||
|             IsPublic            = false | ||||
|             TimeZoneId          = TimeZoneId "America/Denver" | ||||
|             PageSize            = 100 | ||||
|             AsOfDateDisplay     = NoDisplay | ||||
|         } | ||||
|         { SmallGroupId = SmallGroupId Guid.Empty | ||||
|           DaysToExpire = 14 | ||||
|           DaysToKeepNew = 7 | ||||
|           LongTermUpdateWeeks = 4 | ||||
|           EmailFromName = "PrayerTracker" | ||||
|           EmailFromAddress = "prayer@bitbadger.solutions" | ||||
|           Fonts = "native" | ||||
|           HeadingColor = "maroon" | ||||
|           LineColor = "navy" | ||||
|           HeadingFontSize = 16 | ||||
|           TextFontSize = 12 | ||||
|           RequestSort = SortByDate | ||||
|           GroupPassword = "" | ||||
|           DefaultEmailType = HtmlFormat | ||||
|           IsPublic = false | ||||
|           TimeZoneId = TimeZoneId "America/Denver" | ||||
|           PageSize = 100 | ||||
|           AsOfDateDisplay = NoDisplay } | ||||
| 
 | ||||
| 
 | ||||
| /// A member of a small group | ||||
| [<NoComparison; NoEquality>] | ||||
| type Member = | ||||
|     {   /// The ID of the small group member | ||||
|         Id : MemberId | ||||
|          | ||||
|     { | ||||
|         /// The ID of the small group member | ||||
|         Id: MemberId | ||||
| 
 | ||||
|         /// The Id of the small group to which this member belongs | ||||
|         SmallGroupId : SmallGroupId | ||||
|          | ||||
|         SmallGroupId: SmallGroupId | ||||
| 
 | ||||
|         /// The name of the member | ||||
|         Name : string | ||||
|          | ||||
|         Name: string | ||||
| 
 | ||||
|         /// The e-mail address for the member | ||||
|         Email : string | ||||
|          | ||||
|         Email: string | ||||
| 
 | ||||
|         /// The type of e-mail preferred by this member | ||||
|         Format : EmailFormat option | ||||
|         Format: EmailFormat option | ||||
|     } | ||||
| 
 | ||||
| /// Functions to support small group members | ||||
| module Member = | ||||
|      | ||||
| 
 | ||||
|     /// An empty member | ||||
|     let empty = | ||||
|         {   Id           = MemberId Guid.Empty | ||||
|             SmallGroupId = SmallGroupId Guid.Empty | ||||
|             Name         = "" | ||||
|             Email        = "" | ||||
|             Format       = None | ||||
|         } | ||||
|         { Id = MemberId Guid.Empty | ||||
|           SmallGroupId = SmallGroupId Guid.Empty | ||||
|           Name = "" | ||||
|           Email = "" | ||||
|           Format = None } | ||||
| 
 | ||||
| 
 | ||||
| /// This represents a single prayer request | ||||
| [<NoComparison; NoEquality>] | ||||
| type PrayerRequest = | ||||
|     {   /// The ID of this request | ||||
|         Id : PrayerRequestId | ||||
|          | ||||
|     { | ||||
|         /// The ID of this request | ||||
|         Id: PrayerRequestId | ||||
| 
 | ||||
|         /// The type of the request | ||||
|         RequestType : PrayerRequestType | ||||
|          | ||||
|         RequestType: PrayerRequestType | ||||
| 
 | ||||
|         /// The ID of 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 : Instant | ||||
|          | ||||
|         EnteredDate: Instant | ||||
| 
 | ||||
|         /// The date/time this request was last updated | ||||
|         UpdatedDate : Instant | ||||
|          | ||||
|         UpdatedDate: Instant | ||||
| 
 | ||||
|         /// The name of the requestor or subject, or title of announcement | ||||
|         Requestor : string option | ||||
|          | ||||
|         Requestor: string option | ||||
| 
 | ||||
|         /// The text of the request | ||||
|         Text : string | ||||
|          | ||||
|         Text: string | ||||
| 
 | ||||
|         /// Whether the chaplain should be notified for this request | ||||
|         NotifyChaplain : bool | ||||
|          | ||||
|         NotifyChaplain: bool | ||||
| 
 | ||||
|         /// Is this request expired? | ||||
|         Expiration : Expiration | ||||
|         Expiration: Expiration | ||||
|     } | ||||
| // functions are below small group functions | ||||
| 
 | ||||
| @ -417,141 +436,147 @@ type PrayerRequest = | ||||
| /// This represents a small group (Sunday School class, Bible study group, etc.) | ||||
| [<NoComparison; NoEquality>] | ||||
| type SmallGroup = | ||||
|     {   /// The ID of this small group | ||||
|         Id : SmallGroupId | ||||
|          | ||||
|     { | ||||
|         /// The ID of this small group | ||||
|         Id: SmallGroupId | ||||
| 
 | ||||
|         /// The church to which this group belongs | ||||
|         ChurchId : ChurchId | ||||
|          | ||||
|         ChurchId: ChurchId | ||||
| 
 | ||||
|         /// The name of the group | ||||
|         Name : string | ||||
|          | ||||
|         Name: string | ||||
| 
 | ||||
|         /// The preferences for the request list | ||||
|         Preferences : ListPreferences | ||||
|         Preferences: ListPreferences | ||||
|     } | ||||
| 
 | ||||
| /// Functions to support small groups | ||||
| module SmallGroup = | ||||
|      | ||||
| 
 | ||||
|     /// An empty small group | ||||
|     let empty = | ||||
|         {   Id          = SmallGroupId Guid.Empty | ||||
|             ChurchId    = ChurchId Guid.Empty | ||||
|             Name        = ""  | ||||
|             Preferences = ListPreferences.empty | ||||
|         } | ||||
|         { Id = SmallGroupId Guid.Empty | ||||
|           ChurchId = ChurchId Guid.Empty | ||||
|           Name = "" | ||||
|           Preferences = ListPreferences.empty } | ||||
| 
 | ||||
|     /// The DateTimeZone for the time zone ID for this small group | ||||
|     let timeZone group = | ||||
|         let tzId = TimeZoneId.toString group.Preferences.TimeZoneId | ||||
|         if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId] | ||||
|         else DateTimeZone.Utc | ||||
|      | ||||
| 
 | ||||
|         if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then | ||||
|             DateTimeZoneProviders.Tzdb[tzId] | ||||
|         else | ||||
|             DateTimeZone.Utc | ||||
| 
 | ||||
|     /// Get the local date/time for this group | ||||
|     let localTimeNow (clock : IClock) group = | ||||
|         if isNull clock then nullArg (nameof clock) | ||||
|     let localTimeNow (clock: IClock) group = | ||||
|         if isNull clock then | ||||
|             nullArg (nameof clock) | ||||
| 
 | ||||
|         clock.GetCurrentInstant().InZone(timeZone group).LocalDateTime | ||||
| 
 | ||||
|     /// Get the local date for this group | ||||
|     let localDateNow clock group = | ||||
|         (localTimeNow clock group).Date | ||||
|     let localDateNow clock group = (localTimeNow clock group).Date | ||||
| 
 | ||||
| 
 | ||||
| /// Functions to support prayer requests | ||||
| module PrayerRequest = | ||||
|      | ||||
| 
 | ||||
|     /// An empty request | ||||
|     let empty = | ||||
|         {   Id             = PrayerRequestId Guid.Empty | ||||
|             RequestType    = CurrentRequest | ||||
|             UserId         = UserId Guid.Empty | ||||
|             SmallGroupId   = SmallGroupId Guid.Empty | ||||
|             EnteredDate    = Instant.MinValue | ||||
|             UpdatedDate    = Instant.MinValue | ||||
|             Requestor      = None | ||||
|             Text           = ""  | ||||
|             NotifyChaplain = false | ||||
|             Expiration     = Automatic | ||||
|         } | ||||
|         { Id = PrayerRequestId Guid.Empty | ||||
|           RequestType = CurrentRequest | ||||
|           UserId = UserId Guid.Empty | ||||
|           SmallGroupId = SmallGroupId Guid.Empty | ||||
|           EnteredDate = Instant.MinValue | ||||
|           UpdatedDate = Instant.MinValue | ||||
|           Requestor = None | ||||
|           Text = "" | ||||
|           NotifyChaplain = false | ||||
|           Expiration = Automatic } | ||||
| 
 | ||||
|     /// Is this request expired? | ||||
|     let isExpired (asOf : LocalDate) group req = | ||||
|     let isExpired (asOf: LocalDate) group req = | ||||
|         match req.Expiration, req.RequestType with | ||||
|         | Forced, _ -> true | ||||
|         | Manual, _  | ||||
|         | Manual, _ | ||||
|         | Automatic, LongTermRequest | ||||
|         | Automatic, Expecting  -> false | ||||
|         | Automatic, Expecting -> false | ||||
|         | Automatic, _ -> | ||||
|             // Automatic expiration | ||||
|             Period.Between(req.UpdatedDate.InZone(SmallGroup.timeZone group).Date, asOf, PeriodUnits.Days).Days | ||||
|                 >= group.Preferences.DaysToExpire | ||||
|             Period | ||||
|                 .Between(req.UpdatedDate.InZone(SmallGroup.timeZone group).Date, asOf, PeriodUnits.Days) | ||||
|                 .Days | ||||
|             >= group.Preferences.DaysToExpire | ||||
| 
 | ||||
|     /// Is an update required for this long-term request? | ||||
|     let updateRequired asOf group req = | ||||
|         if isExpired asOf group req then false | ||||
|         else asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks | ||||
|                 >= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date | ||||
|         if isExpired asOf group req then | ||||
|             false | ||||
|         else | ||||
|             asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks | ||||
|             >= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date | ||||
| 
 | ||||
| 
 | ||||
| /// This represents a user of PrayerTracker | ||||
| [<NoComparison; NoEquality>] | ||||
| type User = | ||||
|     {   /// The ID of this user | ||||
|         Id : UserId | ||||
|          | ||||
|     { | ||||
|         /// The ID of this user | ||||
|         Id: UserId | ||||
| 
 | ||||
|         /// The first name of this user | ||||
|         FirstName : string | ||||
|          | ||||
|         FirstName: string | ||||
| 
 | ||||
|         /// The last name of this user | ||||
|         LastName : string | ||||
|          | ||||
|         LastName: string | ||||
| 
 | ||||
|         /// The e-mail address of the user | ||||
|         Email : string | ||||
|          | ||||
|         Email: string | ||||
| 
 | ||||
|         /// Whether this user is a PrayerTracker system administrator | ||||
|         IsAdmin : bool | ||||
|          | ||||
|         IsAdmin: bool | ||||
| 
 | ||||
|         /// The user's hashed password | ||||
|         PasswordHash : string | ||||
|          | ||||
|         PasswordHash: string | ||||
| 
 | ||||
|         /// The last time the user was seen (set whenever the user is loaded into a session) | ||||
|         LastSeen : Instant option | ||||
|         LastSeen: Instant option | ||||
|     } | ||||
| with | ||||
| 
 | ||||
|     /// The full name of the user | ||||
|     member this.Name = | ||||
|         $"{this.FirstName} {this.LastName}" | ||||
|     member this.Name = $"{this.FirstName} {this.LastName}" | ||||
| 
 | ||||
| /// Functions to support users | ||||
| module User = | ||||
|      | ||||
| 
 | ||||
|     /// An empty user | ||||
|     let empty = | ||||
|         {   Id           = UserId Guid.Empty | ||||
|             FirstName    = "" | ||||
|             LastName     = "" | ||||
|             Email        = "" | ||||
|             IsAdmin      = false | ||||
|             PasswordHash = "" | ||||
|             LastSeen     = None | ||||
|         } | ||||
|         { Id = UserId Guid.Empty | ||||
|           FirstName = "" | ||||
|           LastName = "" | ||||
|           Email = "" | ||||
|           IsAdmin = false | ||||
|           PasswordHash = "" | ||||
|           LastSeen = None } | ||||
| 
 | ||||
| 
 | ||||
| /// Cross-reference between user and small group | ||||
| [<NoComparison; NoEquality>] | ||||
| type UserSmallGroup = | ||||
|     {   /// The Id of the user who has access to the small group | ||||
|         UserId : UserId | ||||
|          | ||||
|     { | ||||
|         /// The Id of the user who has access to the small group | ||||
|         UserId: UserId | ||||
| 
 | ||||
|         /// The Id of the small group to which the user has access | ||||
|         SmallGroupId : SmallGroupId | ||||
|         SmallGroupId: SmallGroupId | ||||
|     } | ||||
| 
 | ||||
| /// Functions to support user/small group cross-reference | ||||
| module UserSmallGroup = | ||||
|      | ||||
| 
 | ||||
|     /// An empty user/small group xref | ||||
|     let empty = | ||||
|         {   UserId       = UserId Guid.Empty | ||||
|             SmallGroupId = SmallGroupId Guid.Empty | ||||
|         } | ||||
|         { UserId = UserId Guid.Empty | ||||
|           SmallGroupId = SmallGroupId Guid.Empty } | ||||
|  | ||||
| @ -8,28 +8,32 @@ open System | ||||
| [<Tests>] | ||||
| let asOfDateDisplayTests = | ||||
|     testList "AsOfDateDisplay" [ | ||||
|         test "NoDisplay code is correct" { | ||||
|             Expect.equal (AsOfDateDisplay.toCode NoDisplay) "N" "The code for NoDisplay should have been \"N\"" | ||||
|         } | ||||
|         test "ShortDate code is correct" { | ||||
|             Expect.equal (AsOfDateDisplay.toCode ShortDate) "S" "The code for ShortDate should have been \"S\"" | ||||
|         } | ||||
|         test "LongDate code is correct" { | ||||
|             Expect.equal (AsOfDateDisplay.toCode LongDate) "L" "The code for LongDate should have been \"N\"" | ||||
|         } | ||||
|         test "fromCode N should return NoDisplay" { | ||||
|             Expect.equal (AsOfDateDisplay.fromCode "N") NoDisplay "\"N\" should have been converted to NoDisplay" | ||||
|         } | ||||
|         test "fromCode S should return ShortDate" { | ||||
|             Expect.equal (AsOfDateDisplay.fromCode "S") ShortDate "\"S\" should have been converted to ShortDate" | ||||
|         } | ||||
|         test "fromCode L should return LongDate" { | ||||
|             Expect.equal (AsOfDateDisplay.fromCode "L") LongDate "\"L\" should have been converted to LongDate" | ||||
|         } | ||||
|         test "fromCode X should raise" { | ||||
|             Expect.throws (fun () -> AsOfDateDisplay.fromCode "X" |> ignore) | ||||
|                 "An unknown code should have raised an exception" | ||||
|         } | ||||
|         testList "ToString" [ | ||||
|             test "NoDisplay code is correct" { | ||||
|                 Expect.equal (string NoDisplay) "N" "The code for NoDisplay should have been \"N\"" | ||||
|             } | ||||
|             test "ShortDate code is correct" { | ||||
|                 Expect.equal (string ShortDate) "S" "The code for ShortDate should have been \"S\"" | ||||
|             } | ||||
|             test "LongDate code is correct" { | ||||
|                 Expect.equal (string LongDate) "L" "The code for LongDate should have been \"N\"" | ||||
|             } | ||||
|         ] | ||||
|         testList "Parse" [ | ||||
|             test "N should return NoDisplay" { | ||||
|                 Expect.equal (AsOfDateDisplay.Parse "N") NoDisplay "\"N\" should have been parsed to NoDisplay" | ||||
|             } | ||||
|             test "S should return ShortDate" { | ||||
|                 Expect.equal (AsOfDateDisplay.Parse "S") ShortDate "\"S\" should have been parsed to ShortDate" | ||||
|             } | ||||
|             test "L should return LongDate" { | ||||
|                 Expect.equal (AsOfDateDisplay.Parse "L") LongDate "\"L\" should have been parsed to LongDate" | ||||
|             } | ||||
|             test "X should raise" { | ||||
|                 Expect.throws (fun () -> AsOfDateDisplay.Parse "X" |> ignore) | ||||
|                     "An unknown code should have raised an exception" | ||||
|             } | ||||
|         ] | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
| @ -49,50 +53,58 @@ let churchTests = | ||||
| [<Tests>] | ||||
| let emailFormatTests = | ||||
|     testList "EmailFormat" [ | ||||
|         test "HtmlFormat code is correct" { | ||||
|             Expect.equal (EmailFormat.toCode HtmlFormat) "H" "The code for HtmlFormat should have been \"H\"" | ||||
|         } | ||||
|         test "PlainTextFormat code is correct" { | ||||
|             Expect.equal (EmailFormat.toCode PlainTextFormat) "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" | ||||
|         } | ||||
|         testList "ToString" [ | ||||
|             test "HtmlFormat code is correct" { | ||||
|                 Expect.equal (string HtmlFormat) "H" "The code for HtmlFormat should have been \"H\"" | ||||
|             } | ||||
|             test "PlainTextFormat code is correct" { | ||||
|                 Expect.equal (string PlainTextFormat) "P" "The code for PlainTextFormat should have been \"P\"" | ||||
|             } | ||||
|         ] | ||||
|         testList "Parse" [ | ||||
|             test "H should return HtmlFormat" { | ||||
|                 Expect.equal (EmailFormat.Parse "H") HtmlFormat "\"H\" should have been converted to HtmlFormat" | ||||
|             } | ||||
|             test "P should return ShortDate" { | ||||
|                 Expect.equal (EmailFormat.Parse "P") PlainTextFormat | ||||
|                     "\"P\" should have been converted to PlainTextFormat" | ||||
|             } | ||||
|             test "Z should raise" { | ||||
|                 Expect.throws (fun () -> EmailFormat.Parse "Z" |> ignore) | ||||
|                     "An unknown code should have raised an exception" | ||||
|             } | ||||
|         ] | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
| let expirationTests = | ||||
|     testList "Expiration" [ | ||||
|         test "Automatic code is correct" { | ||||
|             Expect.equal (Expiration.toCode Automatic) "A" "The code for Automatic should have been \"A\"" | ||||
|         } | ||||
|         test "Manual code is correct" { | ||||
|             Expect.equal (Expiration.toCode Manual) "M" "The code for Manual should have been \"M\"" | ||||
|         } | ||||
|         test "Forced code is correct" { | ||||
|             Expect.equal (Expiration.toCode Forced) "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" | ||||
|         } | ||||
|         testList "ToString" [ | ||||
|             test "Automatic code is correct" { | ||||
|                 Expect.equal (string Automatic) "A" "The code for Automatic should have been \"A\"" | ||||
|             } | ||||
|             test "Manual code is correct" { | ||||
|                 Expect.equal (string Manual) "M" "The code for Manual should have been \"M\"" | ||||
|             } | ||||
|             test "Forced code is correct" { | ||||
|                 Expect.equal (string Forced) "F" "The code for Forced should have been \"F\"" | ||||
|             } | ||||
|         ] | ||||
|         testList "Parse" [ | ||||
|             test "A should return Automatic" { | ||||
|                 Expect.equal (Expiration.Parse "A") Automatic "\"A\" should have been converted to Automatic" | ||||
|             } | ||||
|             test "M should return Manual" { | ||||
|                 Expect.equal (Expiration.Parse "M") Manual "\"M\" should have been converted to Manual" | ||||
|             } | ||||
|             test "F should return Forced" { | ||||
|                 Expect.equal (Expiration.Parse "F") Forced "\"F\" should have been converted to Forced" | ||||
|             } | ||||
|             test "fromCode V should raise" { | ||||
|                 Expect.throws (fun () -> Expiration.Parse "V" |> ignore) | ||||
|                     "An unknown code should have raised an exception" | ||||
|             } | ||||
|         ] | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
| @ -223,68 +235,74 @@ let prayerRequestTests = | ||||
| [<Tests>] | ||||
| let prayerRequestTypeTests = | ||||
|     testList "PrayerRequestType" [ | ||||
|         test "CurrentRequest code is correct" { | ||||
|             Expect.equal (PrayerRequestType.toCode CurrentRequest) "C" | ||||
|                 "The code for CurrentRequest should have been \"C\"" | ||||
|         } | ||||
|         test "LongTermRequest code is correct" { | ||||
|             Expect.equal (PrayerRequestType.toCode LongTermRequest) "L" | ||||
|                 "The code for LongTermRequest should have been \"L\"" | ||||
|         } | ||||
|         test "PraiseReport code is correct" { | ||||
|             Expect.equal (PrayerRequestType.toCode PraiseReport) "P" "The code for PraiseReport should have been \"P\"" | ||||
|         } | ||||
|         test "Expecting code is correct" { | ||||
|             Expect.equal (PrayerRequestType.toCode Expecting) "E" "The code for Expecting should have been \"E\"" | ||||
|         } | ||||
|         test "Announcement code is correct" { | ||||
|             Expect.equal (PrayerRequestType.toCode Announcement) "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" | ||||
|         } | ||||
|         testList "ToString" [ | ||||
|             test "CurrentRequest code is correct" { | ||||
|                 Expect.equal (string CurrentRequest) "C" "The code for CurrentRequest should have been \"C\"" | ||||
|             } | ||||
|             test "LongTermRequest code is correct" { | ||||
|                 Expect.equal (string LongTermRequest) "L" "The code for LongTermRequest should have been \"L\"" | ||||
|             } | ||||
|             test "PraiseReport code is correct" { | ||||
|                 Expect.equal (string PraiseReport) "P" "The code for PraiseReport should have been \"P\"" | ||||
|             } | ||||
|             test "Expecting code is correct" { | ||||
|                 Expect.equal (string Expecting) "E" "The code for Expecting should have been \"E\"" | ||||
|             } | ||||
|             test "Announcement code is correct" { | ||||
|                 Expect.equal (string Announcement) "A" "The code for Announcement should have been \"A\"" | ||||
|             } | ||||
|         ] | ||||
|         testList "Parse" [ | ||||
|             test "C should return CurrentRequest" { | ||||
|                 Expect.equal (PrayerRequestType.Parse "C") CurrentRequest | ||||
|                     "\"C\" should have been converted to CurrentRequest" | ||||
|             } | ||||
|             test "L should return LongTermRequest" { | ||||
|                 Expect.equal (PrayerRequestType.Parse "L") LongTermRequest | ||||
|                     "\"L\" should have been converted to LongTermRequest" | ||||
|             } | ||||
|             test "P should return PraiseReport" { | ||||
|                 Expect.equal (PrayerRequestType.Parse "P") PraiseReport | ||||
|                     "\"P\" should have been converted to PraiseReport" | ||||
|             } | ||||
|             test "E should return Expecting" { | ||||
|                 Expect.equal (PrayerRequestType.Parse "E") Expecting "\"E\" should have been converted to Expecting" | ||||
|             } | ||||
|             test "A should return Announcement" { | ||||
|                 Expect.equal (PrayerRequestType.Parse "A") Announcement | ||||
|                     "\"A\" should have been converted to Announcement" | ||||
|             } | ||||
|             test "R should raise" { | ||||
|                 Expect.throws (fun () -> PrayerRequestType.Parse "R" |> ignore) | ||||
|                     "An unknown code should have raised an exception" | ||||
|             } | ||||
|         ] | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
| let requestSortTests = | ||||
|     testList "RequestSort" [ | ||||
|         test "SortByDate code is correct" { | ||||
|             Expect.equal (RequestSort.toCode SortByDate) "D" "The code for SortByDate should have been \"D\"" | ||||
|         } | ||||
|         test "SortByRequestor code is correct" { | ||||
|             Expect.equal (RequestSort.toCode SortByRequestor) "R" "The code for SortByRequestor should have been \"R\"" | ||||
|         } | ||||
|         test "fromCode D should return SortByDate" { | ||||
|             Expect.equal (RequestSort.fromCode "D") SortByDate "\"D\" should have been converted to SortByDate" | ||||
|         } | ||||
|         test "fromCode R should return SortByRequestor" { | ||||
|             Expect.equal (RequestSort.fromCode "R") SortByRequestor | ||||
|                 "\"R\" should have been converted to SortByRequestor" | ||||
|         } | ||||
|         test "fromCode Q should raise" { | ||||
|             Expect.throws (fun () -> RequestSort.fromCode "Q" |> ignore) | ||||
|                 "An unknown code should have raised an exception" | ||||
|         } | ||||
|         testList "ToString" [ | ||||
|             test "SortByDate code is correct" { | ||||
|                 Expect.equal (string SortByDate) "D" "The code for SortByDate should have been \"D\"" | ||||
|             } | ||||
|             test "SortByRequestor code is correct" { | ||||
|                 Expect.equal (string SortByRequestor) "R" "The code for SortByRequestor should have been \"R\"" | ||||
|             } | ||||
|         ] | ||||
|         testList "Parse" [ | ||||
|             test "D should return SortByDate" { | ||||
|                 Expect.equal (RequestSort.Parse "D") SortByDate "\"D\" should have been converted to SortByDate" | ||||
|             } | ||||
|             test "R should return SortByRequestor" { | ||||
|                 Expect.equal (RequestSort.Parse "R") SortByRequestor | ||||
|                     "\"R\" should have been converted to SortByRequestor" | ||||
|             } | ||||
|             test "Q should raise" { | ||||
|                 Expect.throws (fun () -> RequestSort.Parse "Q" |> ignore) | ||||
|                     "An unknown code should have raised an exception" | ||||
|             } | ||||
|         ] | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
|  | ||||
| @ -22,12 +22,9 @@ module ReferenceListTests = | ||||
|             test "has all three options listed" { | ||||
|                 let asOf = ReferenceList.asOfDateList _s | ||||
|                 Expect.hasCountOf asOf 3u countAll "There should have been 3 as-of choices returned" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = AsOfDateDisplay.toCode NoDisplay) | ||||
|                     "The option for no display was not found" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = AsOfDateDisplay.toCode ShortDate) | ||||
|                     "The option for a short date was not found" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = AsOfDateDisplay.toCode LongDate) | ||||
|                     "The option for a full date was not found" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = string NoDisplay) "The option for no display was not found" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = string ShortDate) "The option for a short date was not found" | ||||
|                 Expect.exists asOf (fun (x, _) -> x = string LongDate) "The option for a full date was not found" | ||||
|             } | ||||
|         ] | ||||
| 
 | ||||
| @ -41,9 +38,9 @@ module ReferenceListTests = | ||||
|                 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) (EmailFormat.toCode HtmlFormat) "The 2nd option should have been HTML" | ||||
|                 Expect.equal (fst nxt) (string HtmlFormat) "The 2nd option should have been HTML" | ||||
|                 let lst = typs |> Seq.last | ||||
|                 Expect.equal (fst lst) (EmailFormat.toCode PlainTextFormat) "The 3rd option should have been plain text" | ||||
|                 Expect.equal (fst lst) (string PlainTextFormat) "The 3rd option should have been plain text" | ||||
|             } | ||||
|         ] | ||||
|      | ||||
| @ -53,19 +50,19 @@ 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, _) -> exp = Expiration.toCode Automatic) | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = string Automatic) | ||||
|                     "The option for automatic expiration was not found" | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = Expiration.toCode Manual) | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = string Manual) | ||||
|                     "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, _) -> exp = Expiration.toCode Automatic) | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = string Automatic) | ||||
|                     "The option for automatic expiration was not found" | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = Expiration.toCode Manual) | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = string Manual) | ||||
|                     "The option for manual expiration was not found" | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = Expiration.toCode Forced) | ||||
|                 Expect.exists exps (fun (exp, _) -> exp = string Forced) | ||||
|                     "The option for immediate expiration was not found" | ||||
|             } | ||||
|         ] | ||||
| @ -240,7 +237,7 @@ let editMemberTests = | ||||
|         } | ||||
|         test "fromMember populates with specific format" { | ||||
|             let edit = EditMember.fromMember { Member.empty with Format = Some HtmlFormat } | ||||
|             Expect.equal edit.Format (EmailFormat.toCode HtmlFormat) "The e-mail format was not filled correctly" | ||||
|             Expect.equal edit.Format (string HtmlFormat) "The e-mail format was not filled correctly" | ||||
|         } | ||||
|         test "empty is as expected" { | ||||
|             let edit = EditMember.empty | ||||
| @ -268,11 +265,10 @@ let editPreferencesTests = | ||||
|             Expect.equal edit.DaysToKeepNew prefs.DaysToKeepNew "The days to keep new were not filled correctly" | ||||
|             Expect.equal edit.LongTermUpdateWeeks prefs.LongTermUpdateWeeks | ||||
|                 "The weeks for update were not filled correctly" | ||||
|             Expect.equal edit.RequestSort (RequestSort.toCode prefs.RequestSort) | ||||
|                 "The request sort was not filled correctly" | ||||
|             Expect.equal edit.RequestSort (string prefs.RequestSort) "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 (EmailFormat.toCode prefs.DefaultEmailType) | ||||
|             Expect.equal edit.DefaultEmailType (string prefs.DefaultEmailType) | ||||
|                 "The default e-mail type was not filled correctly" | ||||
|             Expect.equal edit.LineColorType "Name" "The heading line color type was not derived correctly" | ||||
|             Expect.equal edit.LineColor prefs.LineColor "The heading line color was not filled correctly" | ||||
| @ -288,8 +284,7 @@ let editPreferencesTests = | ||||
|             Expect.equal edit.Visibility GroupVisibility.PrivateList | ||||
|                 "The list visibility was not derived correctly" | ||||
|             Expect.equal edit.PageSize prefs.PageSize "The page size was not filled correctly" | ||||
|             Expect.equal edit.AsOfDate (AsOfDateDisplay.toCode prefs.AsOfDateDisplay) | ||||
|                 "The as-of date display was not filled correctly" | ||||
|             Expect.equal edit.AsOfDate (string prefs.AsOfDateDisplay) "The as-of date display was not filled correctly" | ||||
|         } | ||||
|         test "fromPreferences succeeds for RGB line color and password-protected list" { | ||||
|             let prefs = { ListPreferences.empty with LineColor = "#ff0000"; GroupPassword = "pw" } | ||||
| @ -326,13 +321,11 @@ let editRequestTests = | ||||
|         test "empty is as expected" { | ||||
|             let mt = EditRequest.empty | ||||
|             Expect.equal mt.RequestId emptyGuid "The request ID should be an empty GUID" | ||||
|             Expect.equal mt.RequestType (PrayerRequestType.toCode CurrentRequest) | ||||
|                 "The request type should have been \"Current\"" | ||||
|             Expect.equal mt.RequestType (string CurrentRequest) "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 (Expiration.toCode Automatic) | ||||
|                 """The expiration should have been "A" (Automatic)""" | ||||
|             Expect.equal mt.Expiration (string Automatic) """The expiration should have been "A" (Automatic)""" | ||||
|             Expect.equal mt.Text "" "The text should have been blank" | ||||
|         } | ||||
|         test "fromRequest succeeds" { | ||||
| @ -346,10 +339,9 @@ let editRequestTests = | ||||
|                 } | ||||
|             let edit = EditRequest.fromRequest req | ||||
|             Expect.equal edit.RequestId (shortGuid req.Id.Value) "The request ID was not filled correctly" | ||||
|             Expect.equal edit.RequestType (PrayerRequestType.toCode req.RequestType) | ||||
|                 "The request type was not filled correctly" | ||||
|             Expect.equal edit.RequestType (string req.RequestType) "The request type was not filled correctly" | ||||
|             Expect.equal edit.Requestor req.Requestor "The requestor was not filled correctly" | ||||
|             Expect.equal edit.Expiration (Expiration.toCode Manual) "The expiration was not filled correctly" | ||||
|             Expect.equal edit.Expiration (string Manual) "The expiration was not filled correctly" | ||||
|             Expect.equal edit.Text req.Text "The text was not filled correctly" | ||||
|         } | ||||
|         test "isNew works for a new request" { | ||||
|  | ||||
| @ -29,7 +29,7 @@ let edit (model : EditRequest) today ctx viewInfo = | ||||
|                 label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                 ReferenceList.requestTypeList s | ||||
|                 |> Seq.ofList | ||||
|                 |> Seq.map (fun (typ, desc) -> PrayerRequestType.toCode typ, desc.Value) | ||||
|                 |> Seq.map (fun (typ, desc) -> string typ, desc.Value) | ||||
|                 |> selectList (nameof model.RequestType) model.RequestType [ _required; _autofocus ] | ||||
|             ] | ||||
|             div [ _inputField ] [ | ||||
|  | ||||
| @ -54,8 +54,8 @@ let announcement isAdmin ctx viewInfo = | ||||
|                 label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] | ||||
|                 reqTypes | ||||
|                 |> Seq.ofList | ||||
|                 |> Seq.map (fun (typ, desc) -> PrayerRequestType.toCode typ, desc.Value) | ||||
|                 |> selectList (nameof model.RequestType) (PrayerRequestType.toCode Announcement) [] | ||||
|                 |> Seq.map (fun (typ, desc) -> string typ, desc.Value) | ||||
|                 |> selectList (nameof model.RequestType) (string Announcement) [] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "send" s["Send Announcement"] ] | ||||
| @ -273,7 +273,7 @@ let members (members : Member list) (emailTypes : Map<string, LocalizedString>) | ||||
|                         div [ _class "cell" ] [ str mbr.Name ] | ||||
|                         div [ _class "cell" ] [ str mbr.Email ] | ||||
|                         div [ _class "cell" ] [ | ||||
|                             locStr emailTypes[defaultArg (mbr.Format |> Option.map EmailFormat.toCode) ""] | ||||
|                             locStr emailTypes[defaultArg (mbr.Format |> Option.map string) ""] | ||||
|                         ] | ||||
|                     ] | ||||
|             ] | ||||
|  | ||||
| @ -11,9 +11,9 @@ module ReferenceList = | ||||
| 
 | ||||
|     /// A localized list of the AsOfDateDisplay DU cases | ||||
|     let asOfDateList (s: IStringLocalizer) = [ | ||||
|         AsOfDateDisplay.toCode NoDisplay, s["Do not display the “as of” date"] | ||||
|         AsOfDateDisplay.toCode ShortDate, s["Display a short “as of” date"] | ||||
|         AsOfDateDisplay.toCode LongDate,  s["Display a full “as of” date"] | ||||
|         string NoDisplay, s["Do not display the “as of” date"] | ||||
|         string ShortDate, s["Display a short “as of” date"] | ||||
|         string LongDate,  s["Display a full “as of” date"] | ||||
|     ] | ||||
| 
 | ||||
|     /// A list of e-mail type options | ||||
| @ -23,16 +23,15 @@ module ReferenceList = | ||||
|             s[match def with HtmlFormat -> "HTML Format" | PlainTextFormat -> "Plain-Text Format"].Value | ||||
|         seq { | ||||
|             "", LocalizedString ("", $"""{s["Group Default"].Value} ({defaultType})""") | ||||
|             EmailFormat.toCode HtmlFormat,      s["HTML Format"] | ||||
|             EmailFormat.toCode PlainTextFormat, s["Plain-Text Format"] | ||||
|             string HtmlFormat,      s["HTML Format"] | ||||
|             string PlainTextFormat, s["Plain-Text Format"] | ||||
|           } | ||||
| 
 | ||||
|     /// A list of expiration options | ||||
|     let expirationList (s: IStringLocalizer) includeExpireNow = [ | ||||
|         Expiration.toCode Automatic, s["Expire Normally"] | ||||
|         Expiration.toCode Manual,    s["Request Never Expires"] | ||||
|         if includeExpireNow then Expiration.toCode Forced, s["Expire Immediately"] | ||||
|     ] | ||||
|     let expirationList (s: IStringLocalizer) includeExpireNow = | ||||
|         [ string Automatic, s["Expire Normally"] | ||||
|           string Manual,    s["Request Never Expires"] | ||||
|           if includeExpireNow then string Forced, s["Expire Immediately"] ] | ||||
| 
 | ||||
|     /// A list of request types | ||||
|     let requestTypeList (s: IStringLocalizer) = [ | ||||
| @ -326,7 +325,7 @@ module EditMember = | ||||
|         {   MemberId = shortGuid mbr.Id.Value | ||||
|             Name     = mbr.Name | ||||
|             Email    = mbr.Email | ||||
|             Format   = match mbr.Format with Some fmt -> EmailFormat.toCode fmt | None -> "" | ||||
|             Format   = mbr.Format |> Option.map string |> Option.defaultValue "" | ||||
|         } | ||||
|      | ||||
|     /// An empty instance | ||||
| @ -413,10 +412,10 @@ with | ||||
|             DaysToExpire        = this.ExpireDays | ||||
|             DaysToKeepNew       = this.DaysToKeepNew | ||||
|             LongTermUpdateWeeks = this.LongTermUpdateWeeks | ||||
|             RequestSort         = RequestSort.fromCode this.RequestSort | ||||
|             RequestSort         = RequestSort.Parse this.RequestSort | ||||
|             EmailFromName       = this.EmailFromName | ||||
|             EmailFromAddress    = this.EmailFromAddress | ||||
|             DefaultEmailType    = EmailFormat.fromCode this.DefaultEmailType | ||||
|             DefaultEmailType    = EmailFormat.Parse this.DefaultEmailType | ||||
|             LineColor           = this.LineColor | ||||
|             HeadingColor        = this.HeadingColor | ||||
|             Fonts               = if this.IsNative || Option.isNone this.Fonts then "native" else this.Fonts.Value | ||||
| @ -426,7 +425,7 @@ with | ||||
|             IsPublic            = isPublic | ||||
|             GroupPassword       = grpPw | ||||
|             PageSize            = this.PageSize | ||||
|             AsOfDateDisplay     = AsOfDateDisplay.fromCode this.AsOfDate | ||||
|             AsOfDateDisplay     = AsOfDateDisplay.Parse this.AsOfDate | ||||
|         } | ||||
| 
 | ||||
| /// Support for the EditPreferences type | ||||
| @ -437,10 +436,10 @@ module EditPreferences = | ||||
|         {   ExpireDays          = prefs.DaysToExpire | ||||
|             DaysToKeepNew       = prefs.DaysToKeepNew | ||||
|             LongTermUpdateWeeks = prefs.LongTermUpdateWeeks | ||||
|             RequestSort         = RequestSort.toCode prefs.RequestSort | ||||
|             RequestSort         = string prefs.RequestSort | ||||
|             EmailFromName       = prefs.EmailFromName | ||||
|             EmailFromAddress    = prefs.EmailFromAddress | ||||
|             DefaultEmailType    = EmailFormat.toCode prefs.DefaultEmailType | ||||
|             DefaultEmailType    = string prefs.DefaultEmailType | ||||
|             LineColorType       = setType prefs.LineColor | ||||
|             LineColor           = prefs.LineColor | ||||
|             HeadingColorType    = setType prefs.HeadingColor | ||||
| @ -452,7 +451,7 @@ module EditPreferences = | ||||
|             TimeZone            = TimeZoneId.toString prefs.TimeZoneId | ||||
|             GroupPassword       = Some prefs.GroupPassword | ||||
|             PageSize            = prefs.PageSize | ||||
|             AsOfDate            = AsOfDateDisplay.toCode prefs.AsOfDateDisplay | ||||
|             AsOfDate            = string prefs.AsOfDateDisplay | ||||
|             Visibility          = | ||||
|                 if   prefs.IsPublic           then GroupVisibility.PublicList | ||||
|                 elif prefs.GroupPassword = "" then GroupVisibility.PrivateList | ||||
| @ -495,11 +494,11 @@ module EditRequest = | ||||
|     /// An empty instance to use for new requests | ||||
|     let empty = | ||||
|         {   RequestId      = emptyGuid | ||||
|             RequestType    = PrayerRequestType.toCode CurrentRequest | ||||
|             RequestType    = string CurrentRequest | ||||
|             EnteredDate    = None | ||||
|             SkipDateUpdate = None | ||||
|             Requestor      = None | ||||
|             Expiration     = Expiration.toCode Automatic | ||||
|             Expiration     = string Automatic | ||||
|             Text           = "" | ||||
|         } | ||||
|      | ||||
| @ -507,9 +506,9 @@ module EditRequest = | ||||
|     let fromRequest (req: PrayerRequest) = | ||||
|         { empty with | ||||
|             RequestId   = shortGuid req.Id.Value | ||||
|             RequestType = PrayerRequestType.toCode req.RequestType | ||||
|             RequestType = string req.RequestType | ||||
|             Requestor   = req.Requestor | ||||
|             Expiration  = Expiration.toCode req.Expiration | ||||
|             Expiration  = string req.Expiration | ||||
|             Text        = req.Text | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -238,10 +238,10 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct | ||||
|             let now  = SmallGroup.localDateNow ctx.Clock group | ||||
|             let updated = | ||||
|                 { pr with | ||||
|                     RequestType = PrayerRequestType.fromCode model.RequestType | ||||
|                     RequestType = PrayerRequestType.Parse model.RequestType | ||||
|                     Requestor   = match model.Requestor with Some x when x.Trim() = "" -> None | x -> x | ||||
|                     Text        = ckEditorToText model.Text | ||||
|                     Expiration  = Expiration.fromCode model.Expiration | ||||
|                     Expiration  = Expiration.Parse model.Expiration | ||||
|                 } | ||||
|                 |> function | ||||
|                 | it when model.IsNew -> | ||||
|  | ||||
| @ -210,7 +210,7 @@ let saveMember : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun n | ||||
|                     { mbr with | ||||
|                         Name   = model.Name | ||||
|                         Email  = model.Email | ||||
|                         Format = String.noneIfBlank model.Format |> Option.map EmailFormat.fromCode } | ||||
|                         Format = String.noneIfBlank model.Format |> Option.map EmailFormat.Parse } | ||||
|             let act = ctx.Strings[if model.IsNew then "Added" else "Updated"].Value.ToLower() | ||||
|             addInfo ctx ctx.Strings["Successfully {0} group member", act] | ||||
|             return! redirectTo false "/small-group/members" next ctx | ||||
| @ -288,7 +288,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> | ||||
|                         Id           = (Guid.NewGuid >> PrayerRequestId) () | ||||
|                         SmallGroupId = group.Id | ||||
|                         UserId       = usr.Id | ||||
|                         RequestType  = (Option.get >> PrayerRequestType.fromCode) model.RequestType | ||||
|                         RequestType  = (Option.get >> PrayerRequestType.Parse) model.RequestType | ||||
|                         Text         = requestText | ||||
|                         EnteredDate  = now.Date.AtStartOfDayInZone(zone).ToInstant() | ||||
|                         UpdatedDate  = now.InZoneLeniently(zone).ToInstant() } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user