485 lines
13 KiB
Forth
485 lines
13 KiB
Forth
namespace JobsJobsJobs.Domain
|
|
|
|
open System
|
|
open Giraffe
|
|
open NodaTime
|
|
|
|
// ~~~ SUPPORT TYPES ~~~ //
|
|
|
|
/// The ID of a user (a citizen of Gitmo Nation)
|
|
type CitizenId = CitizenId of Guid
|
|
|
|
/// Support functions for citizen IDs
|
|
module CitizenId =
|
|
|
|
/// Create a new citizen ID
|
|
let create () = (Guid.NewGuid >> CitizenId) ()
|
|
|
|
/// A string representation of a citizen ID
|
|
let toString = function CitizenId it -> ShortGuid.fromGuid it
|
|
|
|
/// Parse a string into a citizen ID
|
|
let ofString = ShortGuid.toGuid >> CitizenId
|
|
|
|
/// Get the GUID value of a citizen ID
|
|
let value = function CitizenId guid -> guid
|
|
|
|
|
|
/// Types of contacts supported by Jobs, Jobs, Jobs
|
|
type ContactType =
|
|
/// E-mail addresses
|
|
| Email
|
|
/// Phone numbers (home, work, cell, etc.)
|
|
| Phone
|
|
/// Websites (personal, social, etc.)
|
|
| Website
|
|
|
|
/// Functions to support contact types
|
|
module ContactType =
|
|
|
|
/// Parse a contact type from a string
|
|
let parse typ =
|
|
match typ with
|
|
| "Email" -> Email
|
|
| "Phone" -> Phone
|
|
| "Website" -> Website
|
|
| it -> invalidOp $"{it} is not a valid contact type"
|
|
|
|
/// Convert a contact type to its string representation
|
|
let toString =
|
|
function
|
|
| Email -> "Email"
|
|
| Phone -> "Phone"
|
|
| Website -> "Website"
|
|
|
|
|
|
/// The ID of a continent
|
|
type ContinentId = ContinentId of Guid
|
|
|
|
/// Support functions for continent IDs
|
|
module ContinentId =
|
|
|
|
/// Create a new continent ID
|
|
let create () = (Guid.NewGuid >> ContinentId) ()
|
|
|
|
/// A string representation of a continent ID
|
|
let toString = function ContinentId it -> ShortGuid.fromGuid it
|
|
|
|
/// Parse a string into a continent ID
|
|
let ofString = ShortGuid.toGuid >> ContinentId
|
|
|
|
/// Get the GUID value of a continent ID
|
|
let value = function ContinentId guid -> guid
|
|
|
|
|
|
/// A string of Markdown text
|
|
type MarkdownString = Text of string
|
|
|
|
/// Support functions for Markdown strings
|
|
module MarkdownString =
|
|
|
|
open Markdig
|
|
|
|
/// The Markdown conversion pipeline (enables all advanced features)
|
|
let private pipeline = MarkdownPipelineBuilder().UseAdvancedExtensions().Build ()
|
|
|
|
/// Convert this Markdown string to HTML
|
|
let toHtml = function Text text -> Markdown.ToHtml (text, pipeline)
|
|
|
|
/// Convert a Markdown string to its string representation
|
|
let toString = function Text text -> text
|
|
|
|
|
|
/// An employment history entry
|
|
[<NoComparison; NoEquality>]
|
|
type EmploymentHistory =
|
|
{ /// The employer for this period of employment
|
|
Employer : string
|
|
|
|
/// The date employment started
|
|
StartDate : LocalDate
|
|
|
|
/// The date employment ended (None implies ongoing employment)
|
|
EndDate : LocalDate option
|
|
|
|
/// The title / position held
|
|
Position : string option
|
|
|
|
/// A description of the duties entailed during this employment
|
|
Description : MarkdownString option
|
|
}
|
|
|
|
/// Support functions for employment history entries
|
|
module EmploymentHistory =
|
|
|
|
let empty =
|
|
{ Employer = ""
|
|
StartDate = LocalDate.FromDateTime DateTime.Today
|
|
EndDate = None
|
|
Position = None
|
|
Description = None
|
|
}
|
|
|
|
|
|
/// The ID of a job listing
|
|
type ListingId = ListingId of Guid
|
|
|
|
/// Support functions for listing IDs
|
|
module ListingId =
|
|
|
|
/// Create a new job listing ID
|
|
let create () = (Guid.NewGuid >> ListingId) ()
|
|
|
|
/// A string representation of a listing ID
|
|
let toString = function ListingId it -> ShortGuid.fromGuid it
|
|
|
|
/// Parse a string into a listing ID
|
|
let ofString = ShortGuid.toGuid >> ListingId
|
|
|
|
/// Get the GUID value of a listing ID
|
|
let value = function ListingId guid -> guid
|
|
|
|
|
|
/// Another way to contact a citizen from this site
|
|
[<NoComparison; NoEquality>]
|
|
type OtherContact =
|
|
{ /// The type of contact
|
|
ContactType : ContactType
|
|
|
|
/// The name of the contact (Email, Mastodon, LinkedIn, etc.)
|
|
Name : string option
|
|
|
|
/// The value for the contact (e-mail address, user name, URL, etc.)
|
|
Value : string
|
|
|
|
/// Whether this contact is visible in public employment profiles and job listings
|
|
IsPublic : bool
|
|
}
|
|
|
|
|
|
/// Visibility options for an employment profile
|
|
type ProfileVisibility =
|
|
/// Profile is only visible to the citizen to whom it belongs
|
|
| Hidden
|
|
/// Profile is only visible to authenticated users
|
|
| Private
|
|
/// Anonymous information is visible to public users
|
|
| Anonymous
|
|
/// The full employment profile is visible to public users
|
|
| Public
|
|
|
|
/// Support functions for profile visibility
|
|
module ProfileVisibility =
|
|
|
|
/// Parse a string into a profile visibility
|
|
let parse viz =
|
|
match viz with
|
|
| "Hidden" -> Hidden
|
|
| "Private" -> Private
|
|
| "Anonymous" -> Anonymous
|
|
| "Public" -> Public
|
|
| it -> invalidOp $"{it} is not a valid profile visibility value"
|
|
|
|
/// Convert a profile visibility to its string representation
|
|
let toString =
|
|
function
|
|
| Hidden -> "Hidden"
|
|
| Private -> "Private"
|
|
| Anonymous -> "Anonymous"
|
|
| Public -> "Public"
|
|
|
|
|
|
/// A skill the job seeker possesses
|
|
[<NoComparison; NoEquality>]
|
|
type Skill =
|
|
{ /// A description of the skill
|
|
Description : string
|
|
|
|
/// Notes regarding this skill (level, duration, etc.)
|
|
Notes : string option
|
|
}
|
|
|
|
|
|
/// The ID of a success report
|
|
type SuccessId = SuccessId of Guid
|
|
|
|
/// Support functions for success report IDs
|
|
module SuccessId =
|
|
|
|
/// Create a new success report ID
|
|
let create () = (Guid.NewGuid >> SuccessId) ()
|
|
|
|
/// A string representation of a success report ID
|
|
let toString = function SuccessId it -> ShortGuid.fromGuid it
|
|
|
|
/// Parse a string into a success report ID
|
|
let ofString = ShortGuid.toGuid >> SuccessId
|
|
|
|
/// Get the GUID value of a success ID
|
|
let value = function SuccessId guid -> guid
|
|
|
|
// ~~~ DOCUMENT TYPES ~~~ //
|
|
|
|
/// A user of Jobs, Jobs, Jobs; a citizen of Gitmo Nation
|
|
[<NoComparison; NoEquality>]
|
|
type Citizen =
|
|
{ /// The ID of the user
|
|
Id : CitizenId
|
|
|
|
/// When the user joined Jobs, Jobs, Jobs
|
|
JoinedOn : Instant
|
|
|
|
/// When the user last logged in
|
|
LastSeenOn : Instant
|
|
|
|
/// The user's e-mail address
|
|
Email : string
|
|
|
|
/// The user's first name
|
|
FirstName : string
|
|
|
|
/// The user's last name
|
|
LastName : string
|
|
|
|
/// The hash of the user's password
|
|
PasswordHash : string
|
|
|
|
/// The name displayed for this user throughout the site
|
|
DisplayName : string option
|
|
|
|
/// The other contacts for this user
|
|
OtherContacts : OtherContact list
|
|
}
|
|
|
|
/// Support functions for citizens
|
|
module Citizen =
|
|
|
|
/// An empty citizen
|
|
let empty = {
|
|
Id = CitizenId Guid.Empty
|
|
JoinedOn = Instant.MinValue
|
|
LastSeenOn = Instant.MinValue
|
|
Email = ""
|
|
FirstName = ""
|
|
LastName = ""
|
|
PasswordHash = ""
|
|
DisplayName = None
|
|
OtherContacts = []
|
|
}
|
|
|
|
/// Get the name of the citizen (either their preferred display name or first/last names)
|
|
let name x =
|
|
match x.DisplayName with Some it -> it | None -> $"{x.FirstName} {x.LastName}"
|
|
|
|
|
|
/// A continent
|
|
[<NoComparison; NoEquality>]
|
|
type Continent =
|
|
{ /// The ID of the continent
|
|
Id : ContinentId
|
|
|
|
/// The name of the continent
|
|
Name : string
|
|
}
|
|
|
|
/// Support functions for continents
|
|
module Continent =
|
|
|
|
/// An empty continent
|
|
let empty ={
|
|
Id = ContinentId Guid.Empty
|
|
Name = ""
|
|
}
|
|
|
|
|
|
/// A job listing
|
|
[<NoComparison; NoEquality>]
|
|
type Listing =
|
|
{ /// The ID of the job listing
|
|
Id : ListingId
|
|
|
|
/// The ID of the citizen who posted the job listing
|
|
CitizenId : CitizenId
|
|
|
|
/// When this job listing was created
|
|
CreatedOn : Instant
|
|
|
|
/// The short title of the job listing
|
|
Title : string
|
|
|
|
/// The ID of the continent on which the job is located
|
|
ContinentId : ContinentId
|
|
|
|
/// The region in which the job is located
|
|
Region : string
|
|
|
|
/// Whether this listing is for remote work
|
|
IsRemote : bool
|
|
|
|
/// Whether this listing has expired
|
|
IsExpired : bool
|
|
|
|
/// When this listing was last updated
|
|
UpdatedOn : Instant
|
|
|
|
/// The details of this job
|
|
Text : MarkdownString
|
|
|
|
/// When this job needs to be filled
|
|
NeededBy : LocalDate option
|
|
|
|
/// Was this job filled as part of its appearance on Jobs, Jobs, Jobs?
|
|
WasFilledHere : bool option
|
|
}
|
|
|
|
/// Support functions for job listings
|
|
module Listing =
|
|
|
|
/// An empty job listing
|
|
let empty = {
|
|
Id = ListingId Guid.Empty
|
|
CitizenId = CitizenId Guid.Empty
|
|
CreatedOn = Instant.MinValue
|
|
Title = ""
|
|
ContinentId = ContinentId Guid.Empty
|
|
Region = ""
|
|
IsRemote = false
|
|
IsExpired = false
|
|
UpdatedOn = Instant.MinValue
|
|
Text = Text ""
|
|
NeededBy = None
|
|
WasFilledHere = None
|
|
}
|
|
|
|
|
|
/// Security settings for a user
|
|
[<NoComparison; NoEquality>]
|
|
type SecurityInfo =
|
|
{ /// The ID of the citizen to whom these settings apply
|
|
Id : CitizenId
|
|
|
|
/// The number of failed log on attempts (reset to 0 on successful log on)
|
|
FailedLogOnAttempts : int
|
|
|
|
/// Whether the account is locked
|
|
AccountLocked : bool
|
|
|
|
/// The token the user must provide to take their desired action
|
|
Token : string option
|
|
|
|
/// The action to which the token applies
|
|
TokenUsage : string option
|
|
|
|
/// When the token expires
|
|
TokenExpires : Instant option
|
|
}
|
|
|
|
/// Functions to support security info
|
|
module SecurityInfo =
|
|
|
|
/// An empty set of security info
|
|
let empty = {
|
|
Id = CitizenId Guid.Empty
|
|
FailedLogOnAttempts = 0
|
|
AccountLocked = false
|
|
Token = None
|
|
TokenUsage = None
|
|
TokenExpires = None
|
|
}
|
|
|
|
|
|
/// A job seeker profile
|
|
[<NoComparison; NoEquality>]
|
|
type Profile =
|
|
{ /// The ID of the citizen to whom this profile belongs
|
|
Id : CitizenId
|
|
|
|
/// The ID of the continent on which the citizen resides
|
|
ContinentId : ContinentId
|
|
|
|
/// The region in which the citizen resides
|
|
Region : string
|
|
|
|
/// Whether this citizen is actively seeking employment
|
|
IsSeekingEmployment : bool
|
|
|
|
/// Whether the citizen is interested in remote work
|
|
IsRemote : bool
|
|
|
|
/// Whether the citizen is interested in full-time work
|
|
IsFullTime : bool
|
|
|
|
/// The citizen's professional biography
|
|
Biography : MarkdownString
|
|
|
|
/// Skills this citizen possesses
|
|
Skills : Skill list
|
|
|
|
/// The citizen's employment history
|
|
History : EmploymentHistory list
|
|
|
|
/// The citizen's experience (topical / chronological)
|
|
Experience : MarkdownString option
|
|
|
|
/// The visibility of this profile
|
|
Visibility : ProfileVisibility
|
|
|
|
/// When the citizen last updated their profile
|
|
LastUpdatedOn : Instant
|
|
}
|
|
|
|
/// Support functions for Profiles
|
|
module Profile =
|
|
|
|
// An empty profile
|
|
let empty = {
|
|
Id = CitizenId Guid.Empty
|
|
ContinentId = ContinentId Guid.Empty
|
|
Region = ""
|
|
IsSeekingEmployment = false
|
|
IsRemote = false
|
|
IsFullTime = false
|
|
Biography = Text ""
|
|
Skills = []
|
|
History = []
|
|
Experience = None
|
|
Visibility = Private
|
|
LastUpdatedOn = Instant.MinValue
|
|
}
|
|
|
|
|
|
/// A record of success finding employment
|
|
[<NoComparison; NoEquality>]
|
|
type Success =
|
|
{ /// The ID of the success report
|
|
Id : SuccessId
|
|
|
|
/// The ID of the citizen who wrote this success report
|
|
CitizenId : CitizenId
|
|
|
|
/// When this success report was recorded
|
|
RecordedOn : Instant
|
|
|
|
/// Whether the success was due, at least in part, to Jobs, Jobs, Jobs
|
|
IsFromHere : bool
|
|
|
|
/// The source of this success (listing or profile)
|
|
Source : string
|
|
|
|
/// The success story
|
|
Story : MarkdownString option
|
|
}
|
|
|
|
/// Support functions for success stories
|
|
module Success =
|
|
|
|
/// An empty success story
|
|
let empty = {
|
|
Id = SuccessId Guid.Empty
|
|
CitizenId = CitizenId Guid.Empty
|
|
RecordedOn = Instant.MinValue
|
|
IsFromHere = false
|
|
Source = ""
|
|
Story = None
|
|
}
|