Convert module funcs to ToString/Parse

This commit is contained in:
Daniel J. Summers 2025-01-30 19:22:45 -05:00
parent 5240b78487
commit facc294d66
9 changed files with 498 additions and 465 deletions

View File

@ -36,9 +36,9 @@ module private Helpers =
IsPublic = row.bool "is_public" IsPublic = row.bool "is_public"
PageSize = row.int "page_size" PageSize = row.int "page_size"
TimeZoneId = TimeZoneId (row.string "time_zone_id") TimeZoneId = TimeZoneId (row.string "time_zone_id")
RequestSort = RequestSort.fromCode (row.string "request_sort") RequestSort = RequestSort.Parse (row.string "request_sort")
DefaultEmailType = EmailFormat.fromCode (row.string "default_email_type") DefaultEmailType = EmailFormat.Parse (row.string "default_email_type")
AsOfDateDisplay = AsOfDateDisplay.fromCode (row.string "as_of_date_display") AsOfDateDisplay = AsOfDateDisplay.Parse (row.string "as_of_date_display")
} }
/// Map a row to a Member instance /// Map a row to a Member instance
@ -47,7 +47,7 @@ module private Helpers =
SmallGroupId = SmallGroupId (row.uuid "small_group_id") SmallGroupId = SmallGroupId (row.uuid "small_group_id")
Name = row.string "member_name" Name = row.string "member_name"
Email = row.string "email" 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 /// Map a row to a Prayer Request instance
@ -60,8 +60,8 @@ module private Helpers =
Requestor = row.stringOrNone "requestor" Requestor = row.stringOrNone "requestor"
Text = row.string "request_text" Text = row.string "request_text"
NotifyChaplain = row.bool "notify_chaplain" NotifyChaplain = row.bool "notify_chaplain"
RequestType = PrayerRequestType.fromCode (row.string "request_type") RequestType = PrayerRequestType.Parse (row.string "request_type")
Expiration = Expiration.fromCode (row.string "expiration") Expiration = Expiration.Parse (row.string "expiration")
} }
/// Map a row to a Small Group instance /// Map a row to a Small Group instance
@ -185,7 +185,7 @@ module Members =
"@groupId", Sql.uuid mbr.SmallGroupId.Value "@groupId", Sql.uuid mbr.SmallGroupId.Value
"@name", Sql.string mbr.Name "@name", Sql.string mbr.Name
"@email", Sql.string mbr.Email "@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 /// Retrieve a small group member by its ID
let tryById (memberId : MemberId) = let tryById (memberId : MemberId) =
@ -257,10 +257,10 @@ module PrayerRequests =
OR request_type = @expecting) OR request_type = @expecting)
AND expiration <> @forced", AND expiration <> @forced",
[ "@asOf", Sql.parameter asOf [ "@asOf", Sql.parameter asOf
"@manual", Sql.string (Expiration.toCode Manual) "@manual", Sql.string (string Manual)
"@longTerm", Sql.string (PrayerRequestType.toCode LongTermRequest) "@longTerm", Sql.string (string LongTermRequest)
"@expecting", Sql.string (PrayerRequestType.toCode Expecting) "@expecting", Sql.string (string Expecting)
"@forced", Sql.string (Expiration.toCode Forced) ] "@forced", Sql.string (string Forced) ]
else "", [] else "", []
Custom.list Custom.list
$"SELECT * $"SELECT *
@ -287,15 +287,15 @@ module PrayerRequests =
notify_chaplain = EXCLUDED.notify_chaplain, notify_chaplain = EXCLUDED.notify_chaplain,
expiration = EXCLUDED.expiration" expiration = EXCLUDED.expiration"
[ "@id", Sql.uuid req.Id.Value [ "@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 "@userId", Sql.uuid req.UserId.Value
"@groupId", Sql.uuid req.SmallGroupId.Value "@groupId", Sql.uuid req.SmallGroupId.Value
"@entered", Sql.parameter (NpgsqlParameter ("@entered", req.EnteredDate)) "@entered", Sql.parameter (NpgsqlParameter("@entered", req.EnteredDate))
"@updated", Sql.parameter (NpgsqlParameter ("@updated", req.UpdatedDate)) "@updated", Sql.parameter (NpgsqlParameter("@updated", req.UpdatedDate))
"@requestor", Sql.stringOrNone req.Requestor "@requestor", Sql.stringOrNone req.Requestor
"@text", Sql.string req.Text "@text", Sql.string req.Text
"@notifyChaplain", Sql.bool req.NotifyChaplain "@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 /// Search prayer requests for the given term
let searchForGroup group searchTerm pageNbr = let searchForGroup group searchTerm pageNbr =
@ -320,8 +320,7 @@ module PrayerRequests =
[ "@updated", Sql.parameter (NpgsqlParameter ("@updated", req.UpdatedDate)) ] [ "@updated", Sql.parameter (NpgsqlParameter ("@updated", req.UpdatedDate)) ]
else "", [] else "", []
Custom.nonQuery $"UPDATE pt.prayer_request SET expiration = @expiration{sql} WHERE id = @id" Custom.nonQuery $"UPDATE pt.prayer_request SET expiration = @expiration{sql} WHERE id = @id"
([ "@expiration", Sql.string (Expiration.toCode req.Expiration) ([ "@expiration", Sql.string (string req.Expiration); "@id", Sql.uuid req.Id.Value ]
"@id", Sql.uuid req.Id.Value ]
|> List.append parameters) |> List.append parameters)
@ -455,13 +454,13 @@ module SmallGroups =
"@lineColor", Sql.string pref.LineColor "@lineColor", Sql.string pref.LineColor
"@headingFontSize", Sql.int pref.HeadingFontSize "@headingFontSize", Sql.int pref.HeadingFontSize
"@textFontSize", Sql.int pref.TextFontSize "@textFontSize", Sql.int pref.TextFontSize
"@requestSort", Sql.string (RequestSort.toCode pref.RequestSort) "@requestSort", Sql.string (string pref.RequestSort)
"@groupPassword", Sql.string pref.GroupPassword "@groupPassword", Sql.string pref.GroupPassword
"@defaultEmailType", Sql.string (EmailFormat.toCode pref.DefaultEmailType) "@defaultEmailType", Sql.string (string pref.DefaultEmailType)
"@isPublic", Sql.bool pref.IsPublic "@isPublic", Sql.bool pref.IsPublic
"@timeZoneId", Sql.string (TimeZoneId.toString pref.TimeZoneId) "@timeZoneId", Sql.string (TimeZoneId.toString pref.TimeZoneId)
"@pageSize", Sql.int pref.PageSize "@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 /// Get a small group by its ID
let tryById (groupId : SmallGroupId) = let tryById (groupId : SmallGroupId) =

View File

@ -11,20 +11,21 @@ type AsOfDateDisplay =
/// The as-of date should be displayed in the culture's long date format /// The as-of date should be displayed in the culture's long date format
| LongDate | LongDate
/// Functions to support as-of date display options /// Convert this to a single-character code
module AsOfDateDisplay = override this.ToString() =
match this with
| NoDisplay -> "N"
| ShortDate -> "S"
| LongDate -> "L"
/// Convert to a DU case from a single-character string /// <summary>Create an <c>AsOfDateDisplay</c> from a single-character code</summary>
let fromCode code = static member Parse code =
match code with match code with
| "N" -> NoDisplay | "N" -> NoDisplay
| "S" -> ShortDate | "S" -> ShortDate
| "L" -> LongDate | "L" -> LongDate
| _ -> invalidArg "code" $"Unknown code {code}" | _ -> invalidArg "code" $"Unknown code {code}"
/// Convert this DU case to a single-character string
let toCode = function NoDisplay -> "N" | ShortDate -> "S" | LongDate -> "L"
/// Acceptable e-mail formats /// Acceptable e-mail formats
type EmailFormat = type EmailFormat =
@ -33,19 +34,19 @@ type EmailFormat =
/// Plain-text e-mail /// Plain-text e-mail
| PlainTextFormat | PlainTextFormat
/// Functions to support e-mail formats /// Convert this to a single-character code
module EmailFormat = override this.ToString() =
match this with
| HtmlFormat -> "H"
| PlainTextFormat -> "P"
/// Convert to a DU case from a single-character string /// <summary>Create an <c>EmailFormat</c> from a single-character code</summary>
let fromCode code = static member Parse code =
match code with match code with
| "H" -> HtmlFormat | "H" -> HtmlFormat
| "P" -> PlainTextFormat | "P" -> PlainTextFormat
| _ -> invalidArg "code" $"Unknown code {code}" | _ -> invalidArg "code" $"Unknown code {code}"
/// Convert this DU case to a single-character string
let toCode = function HtmlFormat -> "H" | PlainTextFormat -> "P"
/// Expiration for requests /// Expiration for requests
type Expiration = type Expiration =
@ -56,20 +57,21 @@ type Expiration =
/// Force immediate expiration /// Force immediate expiration
| Forced | Forced
/// Functions to support expirations /// Convert this to a single-character code
module Expiration = override this.ToString() =
match this with
| Automatic -> "A"
| Manual -> "M"
| Forced -> "F"
/// Convert to a DU case from a single-character string /// <summary>Create an <c>Expiration</c> from a single-character code</summary>
let fromCode code = static member Parse code =
match code with match code with
| "A" -> Automatic | "A" -> Automatic
| "M" -> Manual | "M" -> Manual
| "F" -> Forced | "F" -> Forced
| _ -> invalidArg "code" $"Unknown code {code}" | _ -> invalidArg "code" $"Unknown code {code}"
/// Convert this DU case to a single-character string
let toCode = function Automatic -> "A" | Manual -> "M" | Forced -> "F"
/// Types of prayer requests /// Types of prayer requests
type PrayerRequestType = type PrayerRequestType =
@ -84,11 +86,17 @@ type PrayerRequestType =
/// Announcements /// Announcements
| Announcement | Announcement
/// Functions to support prayer request types /// Convert this to a single-character code
module PrayerRequestType = override this.ToString() =
match this with
| CurrentRequest -> "C"
| LongTermRequest -> "L"
| Expecting -> "E"
| PraiseReport -> "P"
| Announcement -> "A"
/// Convert to a DU case from a single-character string /// <summary>Create a <c>PrayerRequestType</c> from a single-character code</summary>
let fromCode code = static member Parse code =
match code with match code with
| "C" -> CurrentRequest | "C" -> CurrentRequest
| "L" -> LongTermRequest | "L" -> LongTermRequest
@ -97,15 +105,6 @@ module PrayerRequestType =
| "A" -> Announcement | "A" -> Announcement
| _ -> invalidArg "code" $"Unknown code {code}" | _ -> 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"
/// How requests should be sorted /// How requests should be sorted
type RequestSort = type RequestSort =
@ -114,52 +113,64 @@ type RequestSort =
/// Sort by requestor/subject, then by date /// Sort by requestor/subject, then by date
| SortByRequestor | SortByRequestor
/// Functions to support request sorts /// Convert this to a single-character code
module RequestSort = override this.ToString() =
match this with
| SortByDate -> "D"
| SortByRequestor -> "R"
/// Convert to a DU case from a single-character string /// <summary>Create a <c>RequestSort</c> from a single-character code</summary>
let fromCode code = static member Parse code =
match code with match code with
| "D" -> SortByDate | "D" -> SortByDate
| "R" -> SortByRequestor | "R" -> SortByRequestor
| _ -> invalidArg "code" $"Unknown code {code}" | _ -> invalidArg "code" $"Unknown code {code}"
/// Convert this DU case to a single-character string
let toCode = function SortByDate -> "D" | SortByRequestor -> "R"
open System open System
/// PK type for the Church entity /// PK type for the Church entity
type ChurchId = type ChurchId =
| ChurchId of Guid | ChurchId of Guid
with
/// The GUID value of the church ID /// 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 /// PK type for the Member entity
type MemberId = type MemberId =
| MemberId of Guid | MemberId of Guid
with
/// The GUID value of the member ID /// 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 /// PK type for the PrayerRequest entity
type PrayerRequestId = type PrayerRequestId =
| PrayerRequestId of Guid | PrayerRequestId of Guid
with
/// The GUID value of the prayer request ID /// 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 /// PK type for the SmallGroup entity
type SmallGroupId = type SmallGroupId =
| SmallGroupId of Guid | SmallGroupId of Guid
with
/// The GUID value of the small group ID /// 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 /// PK type for the TimeZone entity
@ -169,49 +180,56 @@ type TimeZoneId = TimeZoneId of string
module TimeZoneId = module TimeZoneId =
/// Convert a time zone ID to its string value /// 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 /// PK type for the User entity
type UserId = type UserId =
| UserId of Guid | UserId of Guid
with
/// The GUID value of the user ID /// 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 --*) (*-- SPECIFIC VIEW TYPES --*)
/// Statistics for churches /// Statistics for churches
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type ChurchStats = 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 /// The number of prayer requests in the church
PrayerRequests : int PrayerRequests: int
/// The number of users who can access small groups in the church /// 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 /// Information needed to display the public/protected request list and small group maintenance pages
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type SmallGroupInfo = type SmallGroupInfo =
{ /// The ID of the small group {
Id : string /// The ID of the small group
Id: string
/// The name of the small group /// The name of the small group
Name : string Name: string
/// The name of the church to which the small group belongs /// 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 /// The ID of the time zone for the small group
TimeZoneId : TimeZoneId TimeZoneId: TimeZoneId
/// Whether the small group has a publicly-available request list /// Whether the small group has a publicly-available request list
IsPublic : bool IsPublic: bool
} }
(*-- ENTITIES --*) (*-- ENTITIES --*)
@ -221,23 +239,24 @@ open NodaTime
/// This represents a church /// This represents a church
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type Church = type Church =
{ /// The ID of this church {
Id : ChurchId /// The ID of this church
Id: ChurchId
/// The name of the church /// The name of the church
Name : string Name: string
/// The city where the church is /// The city where the church is
City : string City: string
/// The 2-letter state or province code for the church's location /// 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? /// Does this church have an active interface with Virtual Prayer Space?
HasVpsInterface : bool HasVpsInterface: bool
/// The address for the interface /// The address for the interface
InterfaceAddress : string option InterfaceAddress: string option
} }
/// Functions to support churches /// Functions to support churches
@ -251,74 +270,74 @@ module Church =
City = "" City = ""
State = "" State = ""
HasVpsInterface = false HasVpsInterface = false
InterfaceAddress = None InterfaceAddress = None }
}
/// Preferences for the form and format of the prayer request list /// Preferences for the form and format of the prayer request list
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type ListPreferences = 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 /// The days after which regular requests expire
DaysToExpire : int DaysToExpire: int
/// The number of days a new or updated request is considered new /// 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 /// 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 /// The name from which e-mails are sent
EmailFromName : string EmailFromName: string
/// The e-mail address from which e-mails are sent /// 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 /// The fonts to use in generating the list of prayer requests
Fonts : string Fonts: string
/// The color for the prayer request list headings /// The color for the prayer request list headings
HeadingColor : string HeadingColor: string
/// The color for the lines offsetting the prayer request list headings /// 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 /// 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 /// The font size for the text on the prayer request list
TextFontSize : int TextFontSize: int
/// The order in which the prayer requests are sorted /// The order in which the prayer requests are sorted
RequestSort : RequestSort RequestSort: RequestSort
/// The password used for "small group login" (view-only request list) /// The password used for "small group login" (view-only request list)
GroupPassword : string GroupPassword: string
/// The default e-mail type for this class /// The default e-mail type for this class
DefaultEmailType : EmailFormat DefaultEmailType: EmailFormat
/// Whether this class makes its request list public /// Whether this class makes its request list public
IsPublic : bool IsPublic: bool
/// The time zone which this class uses (use tzdata names) /// The time zone which this class uses (use tzdata names)
TimeZoneId : TimeZoneId TimeZoneId: TimeZoneId
/// The number of requests displayed per page /// The number of requests displayed per page
PageSize : int PageSize: int
/// How the as-of date should be automatically displayed /// 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) /// The list of fonts to use when displaying request lists (converts "native" to native font stack)
member this.FontStack = member this.FontStack =
if this.Fonts = "native" then if this.Fonts = "native" then
"""system-ui,-apple-system,"Segoe UI",Roboto,Ubuntu,"Liberation Sans",Cantarell,"Helvetica Neue",sans-serif""" """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 /// Functions to support list preferences
module ListPreferences = module ListPreferences =
@ -342,27 +361,27 @@ module ListPreferences =
IsPublic = false IsPublic = false
TimeZoneId = TimeZoneId "America/Denver" TimeZoneId = TimeZoneId "America/Denver"
PageSize = 100 PageSize = 100
AsOfDateDisplay = NoDisplay AsOfDateDisplay = NoDisplay }
}
/// A member of a small group /// A member of a small group
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type Member = 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 /// The Id of the small group to which this member belongs
SmallGroupId : SmallGroupId SmallGroupId: SmallGroupId
/// The name of the member /// The name of the member
Name : string Name: string
/// The e-mail address for the member /// The e-mail address for the member
Email : string Email: string
/// The type of e-mail preferred by this member /// The type of e-mail preferred by this member
Format : EmailFormat option Format: EmailFormat option
} }
/// Functions to support small group members /// Functions to support small group members
@ -374,42 +393,42 @@ module Member =
SmallGroupId = SmallGroupId Guid.Empty SmallGroupId = SmallGroupId Guid.Empty
Name = "" Name = ""
Email = "" Email = ""
Format = None Format = None }
}
/// This represents a single prayer request /// This represents a single prayer request
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type PrayerRequest = type PrayerRequest =
{ /// The ID of this request {
Id : PrayerRequestId /// The ID of this request
Id: PrayerRequestId
/// The type of the request /// The type of the request
RequestType : PrayerRequestType RequestType: PrayerRequestType
/// The ID of the user who entered the request /// The ID of the user who entered the request
UserId : UserId UserId: UserId
/// The small group to which this request belongs /// The small group to which this request belongs
SmallGroupId : SmallGroupId SmallGroupId: SmallGroupId
/// The date/time on which this request was entered /// The date/time on which this request was entered
EnteredDate : Instant EnteredDate: Instant
/// The date/time this request was last updated /// The date/time this request was last updated
UpdatedDate : Instant UpdatedDate: Instant
/// The name of the requestor or subject, or title of announcement /// The name of the requestor or subject, or title of announcement
Requestor : string option Requestor: string option
/// The text of the request /// The text of the request
Text : string Text: string
/// Whether the chaplain should be notified for this request /// Whether the chaplain should be notified for this request
NotifyChaplain : bool NotifyChaplain: bool
/// Is this request expired? /// Is this request expired?
Expiration : Expiration Expiration: Expiration
} }
// functions are below small group functions // functions are below small group functions
@ -417,17 +436,18 @@ type PrayerRequest =
/// This represents a small group (Sunday School class, Bible study group, etc.) /// This represents a small group (Sunday School class, Bible study group, etc.)
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type SmallGroup = 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 /// The church to which this group belongs
ChurchId : ChurchId ChurchId: ChurchId
/// The name of the group /// The name of the group
Name : string Name: string
/// The preferences for the request list /// The preferences for the request list
Preferences : ListPreferences Preferences: ListPreferences
} }
/// Functions to support small groups /// Functions to support small groups
@ -438,23 +458,26 @@ module SmallGroup =
{ Id = SmallGroupId Guid.Empty { Id = SmallGroupId Guid.Empty
ChurchId = ChurchId Guid.Empty ChurchId = ChurchId Guid.Empty
Name = "" Name = ""
Preferences = ListPreferences.empty Preferences = ListPreferences.empty }
}
/// The DateTimeZone for the time zone ID for this small group /// The DateTimeZone for the time zone ID for this small group
let timeZone group = let timeZone group =
let tzId = TimeZoneId.toString group.Preferences.TimeZoneId 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 /// Get the local date/time for this group
let localTimeNow (clock : IClock) group = let localTimeNow (clock: IClock) group =
if isNull clock then nullArg (nameof clock) if isNull clock then
nullArg (nameof clock)
clock.GetCurrentInstant().InZone(timeZone group).LocalDateTime clock.GetCurrentInstant().InZone(timeZone group).LocalDateTime
/// Get the local date for this group /// Get the local date for this group
let localDateNow clock group = let localDateNow clock group = (localTimeNow clock group).Date
(localTimeNow clock group).Date
/// Functions to support prayer requests /// Functions to support prayer requests
@ -471,11 +494,10 @@ module PrayerRequest =
Requestor = None Requestor = None
Text = "" Text = ""
NotifyChaplain = false NotifyChaplain = false
Expiration = Automatic Expiration = Automatic }
}
/// Is this request expired? /// Is this request expired?
let isExpired (asOf : LocalDate) group req = let isExpired (asOf: LocalDate) group req =
match req.Expiration, req.RequestType with match req.Expiration, req.RequestType with
| Forced, _ -> true | Forced, _ -> true
| Manual, _ | Manual, _
@ -483,44 +505,48 @@ module PrayerRequest =
| Automatic, Expecting -> false | Automatic, Expecting -> false
| Automatic, _ -> | Automatic, _ ->
// Automatic expiration // Automatic expiration
Period.Between(req.UpdatedDate.InZone(SmallGroup.timeZone group).Date, asOf, PeriodUnits.Days).Days Period
.Between(req.UpdatedDate.InZone(SmallGroup.timeZone group).Date, asOf, PeriodUnits.Days)
.Days
>= group.Preferences.DaysToExpire >= group.Preferences.DaysToExpire
/// Is an update required for this long-term request? /// Is an update required for this long-term request?
let updateRequired asOf group req = let updateRequired asOf group req =
if isExpired asOf group req then false if isExpired asOf group req then
else asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks false
else
asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks
>= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date >= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date
/// This represents a user of PrayerTracker /// This represents a user of PrayerTracker
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type User = type User =
{ /// The ID of this user {
Id : UserId /// The ID of this user
Id: UserId
/// The first name of this user /// The first name of this user
FirstName : string FirstName: string
/// The last name of this user /// The last name of this user
LastName : string LastName: string
/// The e-mail address of the user /// The e-mail address of the user
Email : string Email: string
/// Whether this user is a PrayerTracker system administrator /// Whether this user is a PrayerTracker system administrator
IsAdmin : bool IsAdmin: bool
/// The user's hashed password /// 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) /// 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 /// The full name of the user
member this.Name = member this.Name = $"{this.FirstName} {this.LastName}"
$"{this.FirstName} {this.LastName}"
/// Functions to support users /// Functions to support users
module User = module User =
@ -533,18 +559,18 @@ module User =
Email = "" Email = ""
IsAdmin = false IsAdmin = false
PasswordHash = "" PasswordHash = ""
LastSeen = None LastSeen = None }
}
/// Cross-reference between user and small group /// Cross-reference between user and small group
[<NoComparison; NoEquality>] [<NoComparison; NoEquality>]
type UserSmallGroup = 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 /// The Id of the small group to which the user has access
SmallGroupId : SmallGroupId SmallGroupId: SmallGroupId
} }
/// Functions to support user/small group cross-reference /// Functions to support user/small group cross-reference
@ -553,5 +579,4 @@ module UserSmallGroup =
/// An empty user/small group xref /// An empty user/small group xref
let empty = let empty =
{ UserId = UserId Guid.Empty { UserId = UserId Guid.Empty
SmallGroupId = SmallGroupId Guid.Empty SmallGroupId = SmallGroupId Guid.Empty }
}

View File

@ -8,29 +8,33 @@ open System
[<Tests>] [<Tests>]
let asOfDateDisplayTests = let asOfDateDisplayTests =
testList "AsOfDateDisplay" [ testList "AsOfDateDisplay" [
testList "ToString" [
test "NoDisplay code is correct" { test "NoDisplay code is correct" {
Expect.equal (AsOfDateDisplay.toCode NoDisplay) "N" "The code for NoDisplay should have been \"N\"" Expect.equal (string NoDisplay) "N" "The code for NoDisplay should have been \"N\""
} }
test "ShortDate code is correct" { test "ShortDate code is correct" {
Expect.equal (AsOfDateDisplay.toCode ShortDate) "S" "The code for ShortDate should have been \"S\"" Expect.equal (string ShortDate) "S" "The code for ShortDate should have been \"S\""
} }
test "LongDate code is correct" { test "LongDate code is correct" {
Expect.equal (AsOfDateDisplay.toCode LongDate) "L" "The code for LongDate should have been \"N\"" Expect.equal (string 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" testList "Parse" [
test "N should return NoDisplay" {
Expect.equal (AsOfDateDisplay.Parse "N") NoDisplay "\"N\" should have been parsed to NoDisplay"
} }
test "fromCode S should return ShortDate" { test "S should return ShortDate" {
Expect.equal (AsOfDateDisplay.fromCode "S") ShortDate "\"S\" should have been converted to ShortDate" Expect.equal (AsOfDateDisplay.Parse "S") ShortDate "\"S\" should have been parsed to ShortDate"
} }
test "fromCode L should return LongDate" { test "L should return LongDate" {
Expect.equal (AsOfDateDisplay.fromCode "L") LongDate "\"L\" should have been converted to LongDate" Expect.equal (AsOfDateDisplay.Parse "L") LongDate "\"L\" should have been parsed to LongDate"
} }
test "fromCode X should raise" { test "X should raise" {
Expect.throws (fun () -> AsOfDateDisplay.fromCode "X" |> ignore) Expect.throws (fun () -> AsOfDateDisplay.Parse "X" |> ignore)
"An unknown code should have raised an exception" "An unknown code should have raised an exception"
} }
] ]
]
[<Tests>] [<Tests>]
let churchTests = let churchTests =
@ -49,51 +53,59 @@ let churchTests =
[<Tests>] [<Tests>]
let emailFormatTests = let emailFormatTests =
testList "EmailFormat" [ testList "EmailFormat" [
testList "ToString" [
test "HtmlFormat code is correct" { test "HtmlFormat code is correct" {
Expect.equal (EmailFormat.toCode HtmlFormat) "H" "The code for HtmlFormat should have been \"H\"" Expect.equal (string HtmlFormat) "H" "The code for HtmlFormat should have been \"H\""
} }
test "PlainTextFormat code is correct" { test "PlainTextFormat code is correct" {
Expect.equal (EmailFormat.toCode PlainTextFormat) "P" "The code for PlainTextFormat should have been \"P\"" Expect.equal (string 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" testList "Parse" [
test "H should return HtmlFormat" {
Expect.equal (EmailFormat.Parse "H") HtmlFormat "\"H\" should have been converted to HtmlFormat"
} }
test "fromCode P should return ShortDate" { test "P should return ShortDate" {
Expect.equal (EmailFormat.fromCode "P") PlainTextFormat Expect.equal (EmailFormat.Parse "P") PlainTextFormat
"\"P\" should have been converted to PlainTextFormat" "\"P\" should have been converted to PlainTextFormat"
} }
test "fromCode Z should raise" { test "Z should raise" {
Expect.throws (fun () -> EmailFormat.fromCode "Z" |> ignore) Expect.throws (fun () -> EmailFormat.Parse "Z" |> ignore)
"An unknown code should have raised an exception" "An unknown code should have raised an exception"
} }
] ]
]
[<Tests>] [<Tests>]
let expirationTests = let expirationTests =
testList "Expiration" [ testList "Expiration" [
testList "ToString" [
test "Automatic code is correct" { test "Automatic code is correct" {
Expect.equal (Expiration.toCode Automatic) "A" "The code for Automatic should have been \"A\"" Expect.equal (string Automatic) "A" "The code for Automatic should have been \"A\""
} }
test "Manual code is correct" { test "Manual code is correct" {
Expect.equal (Expiration.toCode Manual) "M" "The code for Manual should have been \"M\"" Expect.equal (string Manual) "M" "The code for Manual should have been \"M\""
} }
test "Forced code is correct" { test "Forced code is correct" {
Expect.equal (Expiration.toCode Forced) "F" "The code for Forced should have been \"F\"" Expect.equal (string 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" testList "Parse" [
test "A should return Automatic" {
Expect.equal (Expiration.Parse "A") Automatic "\"A\" should have been converted to Automatic"
} }
test "fromCode M should return Manual" { test "M should return Manual" {
Expect.equal (Expiration.fromCode "M") Manual "\"M\" should have been converted to Manual" Expect.equal (Expiration.Parse "M") Manual "\"M\" should have been converted to Manual"
} }
test "fromCode F should return Forced" { test "F should return Forced" {
Expect.equal (Expiration.fromCode "F") Forced "\"F\" should have been converted to Forced" Expect.equal (Expiration.Parse "F") Forced "\"F\" should have been converted to Forced"
} }
test "fromCode V should raise" { test "fromCode V should raise" {
Expect.throws (fun () -> Expiration.fromCode "V" |> ignore) Expect.throws (fun () -> Expiration.Parse "V" |> ignore)
"An unknown code should have raised an exception" "An unknown code should have raised an exception"
} }
] ]
]
[<Tests>] [<Tests>]
let listPreferencesTests = let listPreferencesTests =
@ -223,69 +235,75 @@ let prayerRequestTests =
[<Tests>] [<Tests>]
let prayerRequestTypeTests = let prayerRequestTypeTests =
testList "PrayerRequestType" [ testList "PrayerRequestType" [
testList "ToString" [
test "CurrentRequest code is correct" { test "CurrentRequest code is correct" {
Expect.equal (PrayerRequestType.toCode CurrentRequest) "C" Expect.equal (string CurrentRequest) "C" "The code for CurrentRequest should have been \"C\""
"The code for CurrentRequest should have been \"C\""
} }
test "LongTermRequest code is correct" { test "LongTermRequest code is correct" {
Expect.equal (PrayerRequestType.toCode LongTermRequest) "L" Expect.equal (string LongTermRequest) "L" "The code for LongTermRequest should have been \"L\""
"The code for LongTermRequest should have been \"L\""
} }
test "PraiseReport code is correct" { test "PraiseReport code is correct" {
Expect.equal (PrayerRequestType.toCode PraiseReport) "P" "The code for PraiseReport should have been \"P\"" Expect.equal (string PraiseReport) "P" "The code for PraiseReport should have been \"P\""
} }
test "Expecting code is correct" { test "Expecting code is correct" {
Expect.equal (PrayerRequestType.toCode Expecting) "E" "The code for Expecting should have been \"E\"" Expect.equal (string Expecting) "E" "The code for Expecting should have been \"E\""
} }
test "Announcement code is correct" { test "Announcement code is correct" {
Expect.equal (PrayerRequestType.toCode Announcement) "A" "The code for Announcement should have been \"A\"" Expect.equal (string Announcement) "A" "The code for Announcement should have been \"A\""
} }
test "fromCode C should return CurrentRequest" { ]
Expect.equal (PrayerRequestType.fromCode "C") CurrentRequest testList "Parse" [
test "C should return CurrentRequest" {
Expect.equal (PrayerRequestType.Parse "C") CurrentRequest
"\"C\" should have been converted to CurrentRequest" "\"C\" should have been converted to CurrentRequest"
} }
test "fromCode L should return LongTermRequest" { test "L should return LongTermRequest" {
Expect.equal (PrayerRequestType.fromCode "L") LongTermRequest Expect.equal (PrayerRequestType.Parse "L") LongTermRequest
"\"L\" should have been converted to LongTermRequest" "\"L\" should have been converted to LongTermRequest"
} }
test "fromCode P should return PraiseReport" { test "P should return PraiseReport" {
Expect.equal (PrayerRequestType.fromCode "P") PraiseReport Expect.equal (PrayerRequestType.Parse "P") PraiseReport
"\"P\" should have been converted to PraiseReport" "\"P\" should have been converted to PraiseReport"
} }
test "fromCode E should return Expecting" { test "E should return Expecting" {
Expect.equal (PrayerRequestType.fromCode "E") Expecting "\"E\" should have been converted to Expecting" Expect.equal (PrayerRequestType.Parse "E") Expecting "\"E\" should have been converted to Expecting"
} }
test "fromCode A should return Announcement" { test "A should return Announcement" {
Expect.equal (PrayerRequestType.fromCode "A") Announcement Expect.equal (PrayerRequestType.Parse "A") Announcement
"\"A\" should have been converted to Announcement" "\"A\" should have been converted to Announcement"
} }
test "fromCode R should raise" { test "R should raise" {
Expect.throws (fun () -> PrayerRequestType.fromCode "R" |> ignore) Expect.throws (fun () -> PrayerRequestType.Parse "R" |> ignore)
"An unknown code should have raised an exception" "An unknown code should have raised an exception"
} }
] ]
]
[<Tests>] [<Tests>]
let requestSortTests = let requestSortTests =
testList "RequestSort" [ testList "RequestSort" [
testList "ToString" [
test "SortByDate code is correct" { test "SortByDate code is correct" {
Expect.equal (RequestSort.toCode SortByDate) "D" "The code for SortByDate should have been \"D\"" Expect.equal (string SortByDate) "D" "The code for SortByDate should have been \"D\""
} }
test "SortByRequestor code is correct" { test "SortByRequestor code is correct" {
Expect.equal (RequestSort.toCode SortByRequestor) "R" "The code for SortByRequestor should have been \"R\"" Expect.equal (string 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" testList "Parse" [
test "D should return SortByDate" {
Expect.equal (RequestSort.Parse "D") SortByDate "\"D\" should have been converted to SortByDate"
} }
test "fromCode R should return SortByRequestor" { test "R should return SortByRequestor" {
Expect.equal (RequestSort.fromCode "R") SortByRequestor Expect.equal (RequestSort.Parse "R") SortByRequestor
"\"R\" should have been converted to SortByRequestor" "\"R\" should have been converted to SortByRequestor"
} }
test "fromCode Q should raise" { test "Q should raise" {
Expect.throws (fun () -> RequestSort.fromCode "Q" |> ignore) Expect.throws (fun () -> RequestSort.Parse "Q" |> ignore)
"An unknown code should have raised an exception" "An unknown code should have raised an exception"
} }
] ]
]
[<Tests>] [<Tests>]
let smallGroupTests = let smallGroupTests =

View File

@ -22,12 +22,9 @@ module ReferenceListTests =
test "has all three options listed" { test "has all three options listed" {
let asOf = ReferenceList.asOfDateList _s let asOf = ReferenceList.asOfDateList _s
Expect.hasCountOf asOf 3u countAll "There should have been 3 as-of choices returned" Expect.hasCountOf asOf 3u countAll "There should have been 3 as-of choices returned"
Expect.exists asOf (fun (x, _) -> x = AsOfDateDisplay.toCode NoDisplay) Expect.exists asOf (fun (x, _) -> x = string NoDisplay) "The option for no display was not found"
"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 = AsOfDateDisplay.toCode ShortDate) Expect.exists asOf (fun (x, _) -> x = string LongDate) "The option for a full date was not found"
"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"
} }
] ]
@ -41,9 +38,9 @@ module ReferenceListTests =
Expect.equal (fst top) "" "The default option should have been blank" 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" Expect.equal (snd top).Value "Group Default (HTML Format)" "The default option label was incorrect"
let nxt = typs |> Seq.skip 1 |> Seq.head 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 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" { test "excludes immediate expiration if not required" {
let exps = ReferenceList.expirationList _s false let exps = ReferenceList.expirationList _s false
Expect.hasCountOf exps 2u countAll "There should have been 2 expiration types returned" 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" "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" "The option for manual expiration was not found"
} }
test "includes immediate expiration if required" { test "includes immediate expiration if required" {
let exps = ReferenceList.expirationList _s true let exps = ReferenceList.expirationList _s true
Expect.hasCountOf exps 3u countAll "There should have been 3 expiration types returned" 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" "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" "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" "The option for immediate expiration was not found"
} }
] ]
@ -240,7 +237,7 @@ let editMemberTests =
} }
test "fromMember populates with specific format" { test "fromMember populates with specific format" {
let edit = EditMember.fromMember { Member.empty with Format = Some HtmlFormat } 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" { test "empty is as expected" {
let edit = EditMember.empty 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.DaysToKeepNew prefs.DaysToKeepNew "The days to keep new were not filled correctly"
Expect.equal edit.LongTermUpdateWeeks prefs.LongTermUpdateWeeks Expect.equal edit.LongTermUpdateWeeks prefs.LongTermUpdateWeeks
"The weeks for update were not filled correctly" "The weeks for update were not filled correctly"
Expect.equal edit.RequestSort (RequestSort.toCode prefs.RequestSort) Expect.equal edit.RequestSort (string prefs.RequestSort) "The request sort was not filled correctly"
"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.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.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" "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.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" 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 Expect.equal edit.Visibility GroupVisibility.PrivateList
"The list visibility was not derived correctly" "The list visibility was not derived correctly"
Expect.equal edit.PageSize prefs.PageSize "The page size was not filled correctly" Expect.equal edit.PageSize prefs.PageSize "The page size was not filled correctly"
Expect.equal edit.AsOfDate (AsOfDateDisplay.toCode prefs.AsOfDateDisplay) Expect.equal edit.AsOfDate (string prefs.AsOfDateDisplay) "The as-of date display was not filled correctly"
"The as-of date display was not filled correctly"
} }
test "fromPreferences succeeds for RGB line color and password-protected list" { test "fromPreferences succeeds for RGB line color and password-protected list" {
let prefs = { ListPreferences.empty with LineColor = "#ff0000"; GroupPassword = "pw" } let prefs = { ListPreferences.empty with LineColor = "#ff0000"; GroupPassword = "pw" }
@ -326,13 +321,11 @@ let editRequestTests =
test "empty is as expected" { test "empty is as expected" {
let mt = EditRequest.empty let mt = EditRequest.empty
Expect.equal mt.RequestId emptyGuid "The request ID should be an empty GUID" Expect.equal mt.RequestId emptyGuid "The request ID should be an empty GUID"
Expect.equal mt.RequestType (PrayerRequestType.toCode CurrentRequest) Expect.equal mt.RequestType (string CurrentRequest) "The request type should have been \"Current\""
"The request type should have been \"Current\""
Expect.isNone mt.EnteredDate "The entered date should have been None" 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.SkipDateUpdate """The "skip date update" flag should have been None"""
Expect.isNone mt.Requestor "The requestor should have been None" Expect.isNone mt.Requestor "The requestor should have been None"
Expect.equal mt.Expiration (Expiration.toCode Automatic) Expect.equal mt.Expiration (string Automatic) """The expiration should have been "A" (Automatic)"""
"""The expiration should have been "A" (Automatic)"""
Expect.equal mt.Text "" "The text should have been blank" Expect.equal mt.Text "" "The text should have been blank"
} }
test "fromRequest succeeds" { test "fromRequest succeeds" {
@ -346,10 +339,9 @@ let editRequestTests =
} }
let edit = EditRequest.fromRequest req let edit = EditRequest.fromRequest req
Expect.equal edit.RequestId (shortGuid req.Id.Value) "The request ID was not filled correctly" Expect.equal edit.RequestId (shortGuid req.Id.Value) "The request ID was not filled correctly"
Expect.equal edit.RequestType (PrayerRequestType.toCode req.RequestType) Expect.equal edit.RequestType (string req.RequestType) "The request type was not filled correctly"
"The request type was not filled correctly"
Expect.equal edit.Requestor req.Requestor "The requestor 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" Expect.equal edit.Text req.Text "The text was not filled correctly"
} }
test "isNew works for a new request" { test "isNew works for a new request" {

View File

@ -29,7 +29,7 @@ let edit (model : EditRequest) today ctx viewInfo =
label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ]
ReferenceList.requestTypeList s ReferenceList.requestTypeList s
|> Seq.ofList |> 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 ] |> selectList (nameof model.RequestType) model.RequestType [ _required; _autofocus ]
] ]
div [ _inputField ] [ div [ _inputField ] [

View File

@ -54,8 +54,8 @@ let announcement isAdmin ctx viewInfo =
label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ] label [ _for (nameof model.RequestType) ] [ locStr s["Request Type"] ]
reqTypes reqTypes
|> Seq.ofList |> 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) (PrayerRequestType.toCode Announcement) [] |> selectList (nameof model.RequestType) (string Announcement) []
] ]
] ]
div [ _fieldRow ] [ submit [] "send" s["Send 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.Name ]
div [ _class "cell" ] [ str mbr.Email ] div [ _class "cell" ] [ str mbr.Email ]
div [ _class "cell" ] [ div [ _class "cell" ] [
locStr emailTypes[defaultArg (mbr.Format |> Option.map EmailFormat.toCode) ""] locStr emailTypes[defaultArg (mbr.Format |> Option.map string) ""]
] ]
] ]
] ]

View File

@ -11,9 +11,9 @@ module ReferenceList =
/// A localized list of the AsOfDateDisplay DU cases /// A localized list of the AsOfDateDisplay DU cases
let asOfDateList (s: IStringLocalizer) = [ let asOfDateList (s: IStringLocalizer) = [
AsOfDateDisplay.toCode NoDisplay, s["Do not display the “as of” date"] string NoDisplay, s["Do not display the “as of” date"]
AsOfDateDisplay.toCode ShortDate, s["Display a short “as of” date"] string ShortDate, s["Display a short “as of” date"]
AsOfDateDisplay.toCode LongDate, s["Display a full “as of” date"] string LongDate, s["Display a full “as of” date"]
] ]
/// A list of e-mail type options /// 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 s[match def with HtmlFormat -> "HTML Format" | PlainTextFormat -> "Plain-Text Format"].Value
seq { seq {
"", LocalizedString ("", $"""{s["Group Default"].Value} ({defaultType})""") "", LocalizedString ("", $"""{s["Group Default"].Value} ({defaultType})""")
EmailFormat.toCode HtmlFormat, s["HTML Format"] string HtmlFormat, s["HTML Format"]
EmailFormat.toCode PlainTextFormat, s["Plain-Text Format"] string PlainTextFormat, s["Plain-Text Format"]
} }
/// A list of expiration options /// A list of expiration options
let expirationList (s: IStringLocalizer) includeExpireNow = [ let expirationList (s: IStringLocalizer) includeExpireNow =
Expiration.toCode Automatic, s["Expire Normally"] [ string Automatic, s["Expire Normally"]
Expiration.toCode Manual, s["Request Never Expires"] string Manual, s["Request Never Expires"]
if includeExpireNow then Expiration.toCode Forced, s["Expire Immediately"] if includeExpireNow then string Forced, s["Expire Immediately"] ]
]
/// A list of request types /// A list of request types
let requestTypeList (s: IStringLocalizer) = [ let requestTypeList (s: IStringLocalizer) = [
@ -326,7 +325,7 @@ module EditMember =
{ MemberId = shortGuid mbr.Id.Value { MemberId = shortGuid mbr.Id.Value
Name = mbr.Name Name = mbr.Name
Email = mbr.Email 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 /// An empty instance
@ -413,10 +412,10 @@ with
DaysToExpire = this.ExpireDays DaysToExpire = this.ExpireDays
DaysToKeepNew = this.DaysToKeepNew DaysToKeepNew = this.DaysToKeepNew
LongTermUpdateWeeks = this.LongTermUpdateWeeks LongTermUpdateWeeks = this.LongTermUpdateWeeks
RequestSort = RequestSort.fromCode this.RequestSort RequestSort = RequestSort.Parse this.RequestSort
EmailFromName = this.EmailFromName EmailFromName = this.EmailFromName
EmailFromAddress = this.EmailFromAddress EmailFromAddress = this.EmailFromAddress
DefaultEmailType = EmailFormat.fromCode this.DefaultEmailType DefaultEmailType = EmailFormat.Parse this.DefaultEmailType
LineColor = this.LineColor LineColor = this.LineColor
HeadingColor = this.HeadingColor HeadingColor = this.HeadingColor
Fonts = if this.IsNative || Option.isNone this.Fonts then "native" else this.Fonts.Value Fonts = if this.IsNative || Option.isNone this.Fonts then "native" else this.Fonts.Value
@ -426,7 +425,7 @@ with
IsPublic = isPublic IsPublic = isPublic
GroupPassword = grpPw GroupPassword = grpPw
PageSize = this.PageSize PageSize = this.PageSize
AsOfDateDisplay = AsOfDateDisplay.fromCode this.AsOfDate AsOfDateDisplay = AsOfDateDisplay.Parse this.AsOfDate
} }
/// Support for the EditPreferences type /// Support for the EditPreferences type
@ -437,10 +436,10 @@ module EditPreferences =
{ ExpireDays = prefs.DaysToExpire { ExpireDays = prefs.DaysToExpire
DaysToKeepNew = prefs.DaysToKeepNew DaysToKeepNew = prefs.DaysToKeepNew
LongTermUpdateWeeks = prefs.LongTermUpdateWeeks LongTermUpdateWeeks = prefs.LongTermUpdateWeeks
RequestSort = RequestSort.toCode prefs.RequestSort RequestSort = string prefs.RequestSort
EmailFromName = prefs.EmailFromName EmailFromName = prefs.EmailFromName
EmailFromAddress = prefs.EmailFromAddress EmailFromAddress = prefs.EmailFromAddress
DefaultEmailType = EmailFormat.toCode prefs.DefaultEmailType DefaultEmailType = string prefs.DefaultEmailType
LineColorType = setType prefs.LineColor LineColorType = setType prefs.LineColor
LineColor = prefs.LineColor LineColor = prefs.LineColor
HeadingColorType = setType prefs.HeadingColor HeadingColorType = setType prefs.HeadingColor
@ -452,7 +451,7 @@ module EditPreferences =
TimeZone = TimeZoneId.toString prefs.TimeZoneId TimeZone = TimeZoneId.toString prefs.TimeZoneId
GroupPassword = Some prefs.GroupPassword GroupPassword = Some prefs.GroupPassword
PageSize = prefs.PageSize PageSize = prefs.PageSize
AsOfDate = AsOfDateDisplay.toCode prefs.AsOfDateDisplay AsOfDate = string prefs.AsOfDateDisplay
Visibility = Visibility =
if prefs.IsPublic then GroupVisibility.PublicList if prefs.IsPublic then GroupVisibility.PublicList
elif prefs.GroupPassword = "" then GroupVisibility.PrivateList elif prefs.GroupPassword = "" then GroupVisibility.PrivateList
@ -495,11 +494,11 @@ module EditRequest =
/// An empty instance to use for new requests /// An empty instance to use for new requests
let empty = let empty =
{ RequestId = emptyGuid { RequestId = emptyGuid
RequestType = PrayerRequestType.toCode CurrentRequest RequestType = string CurrentRequest
EnteredDate = None EnteredDate = None
SkipDateUpdate = None SkipDateUpdate = None
Requestor = None Requestor = None
Expiration = Expiration.toCode Automatic Expiration = string Automatic
Text = "" Text = ""
} }
@ -507,9 +506,9 @@ module EditRequest =
let fromRequest (req: PrayerRequest) = let fromRequest (req: PrayerRequest) =
{ empty with { empty with
RequestId = shortGuid req.Id.Value RequestId = shortGuid req.Id.Value
RequestType = PrayerRequestType.toCode req.RequestType RequestType = string req.RequestType
Requestor = req.Requestor Requestor = req.Requestor
Expiration = Expiration.toCode req.Expiration Expiration = string req.Expiration
Text = req.Text Text = req.Text
} }

View File

@ -238,10 +238,10 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
let now = SmallGroup.localDateNow ctx.Clock group let now = SmallGroup.localDateNow ctx.Clock group
let updated = let updated =
{ pr with { 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 Requestor = match model.Requestor with Some x when x.Trim() = "" -> None | x -> x
Text = ckEditorToText model.Text Text = ckEditorToText model.Text
Expiration = Expiration.fromCode model.Expiration Expiration = Expiration.Parse model.Expiration
} }
|> function |> function
| it when model.IsNew -> | it when model.IsNew ->

View File

@ -210,7 +210,7 @@ let saveMember : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun n
{ mbr with { mbr with
Name = model.Name Name = model.Name
Email = model.Email 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() let act = ctx.Strings[if model.IsNew then "Added" else "Updated"].Value.ToLower()
addInfo ctx ctx.Strings["Successfully {0} group member", act] addInfo ctx ctx.Strings["Successfully {0} group member", act]
return! redirectTo false "/small-group/members" next ctx return! redirectTo false "/small-group/members" next ctx
@ -288,7 +288,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
Id = (Guid.NewGuid >> PrayerRequestId) () Id = (Guid.NewGuid >> PrayerRequestId) ()
SmallGroupId = group.Id SmallGroupId = group.Id
UserId = usr.Id UserId = usr.Id
RequestType = (Option.get >> PrayerRequestType.fromCode) model.RequestType RequestType = (Option.get >> PrayerRequestType.Parse) model.RequestType
Text = requestText Text = requestText
EnteredDate = now.Date.AtStartOfDayInZone(zone).ToInstant() EnteredDate = now.Date.AtStartOfDayInZone(zone).ToInstant()
UpdatedDate = now.InZoneLeniently(zone).ToInstant() } UpdatedDate = now.InZoneLeniently(zone).ToInstant() }