WIP on person table, podcast/ep assignment (#17)

This commit is contained in:
2026-08-02 17:33:38 -04:00
parent 05a810dfc6
commit d9e7c771ba
18 changed files with 471 additions and 101 deletions
+30
View File
@@ -143,6 +143,36 @@ type Page = {
OpenGraph = None }
/// <summary>A person (associated with podcasting)</summary>
[<CLIMutable; NoComparison; NoEquality>]
type Person = {
/// <summary>The ID of this person</summary>
Id: PersonId
/// <summary>The ID of the web log to which this person belongs</summary>
WebLogId: WebLogId
/// <summary>The person's name</summary>
Name: string
/// <summary>A URL for more information about this person</summary>
/// <remarks>Non-absolute URLs will be treated as a permalink</remarks>
Url: string option
/// <summary>A URL with an image / avatar for this person</summary>
/// <remarks>Non-absolute URLs will be treated as a permalink</remarks>
ImageUrl: string option
} with
/// <summary>An empty person entry</summary>
static member Empty =
{ Id = PersonId.Empty
WebLogId = WebLogId.Empty
Name = ""
Url = None
ImageUrl = None }
/// <summary>A web log post</summary>
[<CLIMutable; NoComparison; NoEquality>]
type Post = {
+116 -1
View File
@@ -402,6 +402,37 @@ type PersonRole =
PersonRole.GroupXref |> List.find (fun it -> fst it = this) |> snd
open System
/// <summary>An identifier for a person</summary>
[<Struct>]
type PersonId =
| PersonId of string
/// <summary>An empty person ID</summary>
static member Empty = PersonId ""
/// <summary>Create a new person ID</summary>
/// <returns>A new person ID</returns>
static member Create =
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..21] |> PersonId
/// <inheritdoc />
override this.ToString() =
match this with PersonId it -> it
/// <summary>The combination of a person and a role</summary>
[<CLIMutable>]
type PersonAssignment = {
/// <summary>The ID of the person to whom this assignment applies</summary>
PersonId: PersonId
/// <summary>The role to which this person is assigned</summary>
Role: PersonRole
}
open NodaTime.Text
/// <summary>A podcast episode</summary>
@@ -462,6 +493,9 @@ type Episode = {
/// <summary>A description of the episode</summary>
EpisodeDescription: string option
/// <summary>The people and roles for this episode</summary>
People: PersonAssignment list option
} with
/// <summary>An empty episode</summary>
@@ -484,7 +518,8 @@ type Episode = {
SeasonNumber = None
SeasonDescription = None
EpisodeNumber = None
EpisodeDescription = None }
EpisodeDescription = None
People = None }
/// <summary>Format a duration for an episode</summary>
/// <returns>A duration formatted in hours, minutes, and seconds</returns>
@@ -529,3 +564,83 @@ type PodcastMedium =
| Newsletter -> "newsletter"
| Blog -> "blog"
/// <summary>Options for a feed that describes a podcast</summary>
[<CLIMutable; NoComparison; NoEquality>]
type PodcastOptions = {
/// <summary>The title of the podcast</summary>
Title: string
/// <summary>A subtitle for the podcast</summary>
Subtitle: string option
/// <summary>The number of items in the podcast feed</summary>
ItemsInFeed: int
/// <summary>A summary of the podcast (iTunes field)</summary>
Summary: string
/// <summary>The display name of the podcast author (iTunes field)</summary>
DisplayedAuthor: string
/// <summary>The e-mail address of the user who registered the podcast at iTunes</summary>
Email: string
/// <summary>The link to the image for the podcast</summary>
/// <remarks>Non-absolute URLs will be treated as a permalink</remarks>
ImageUrl: string
/// <summary>The category from Apple Podcasts (iTunes) under which this podcast is categorized</summary>
AppleCategory: string
/// <summary>
/// A further refinement of the categorization of this podcast (Apple Podcasts/iTunes field / values)
/// </summary>
AppleSubcategory: string option
/// <summary>The explictness rating (iTunes field)</summary>
Explicit: ExplicitRating
/// <summary>The default media type for files in this podcast</summary>
DefaultMediaType: string option
/// <summary>
/// The base URL for relative URL media files for this podcast (optional; defaults to web log base)
/// </summary>
MediaBaseUrl: string option
/// <summary>A GUID for this podcast</summary>
PodcastGuid: Guid option
/// <summary>A URL at which information on supporting the podcast may be found (supports permalinks)</summary>
FundingUrl: string option
/// <summary>The text to be displayed in the funding item within the feed</summary>
FundingText: string option
/// <summary>The medium (what the podcast IS, not what it is ABOUT)</summary>
Medium: PodcastMedium option
/// <summary>The people and roles for the podcast overall</summary>
People: PersonAssignment list option
} with
/// <summary>A default set of podcast options</summary>
static member Empty =
{ Title = ""
Subtitle = None
ItemsInFeed = 0
Summary = ""
DisplayedAuthor = ""
Email = ""
ImageUrl = ""
AppleCategory = ""
AppleSubcategory = None
Explicit = No
DefaultMediaType = None
MediaBaseUrl = None
PodcastGuid = None
FundingUrl = None
FundingText = None
Medium = None
People = None }
+13 -89
View File
@@ -552,19 +552,6 @@ type OpenGraphProperties = {
}
/// <summary>A permanent link</summary>
[<Struct>]
type Permalink =
| Permalink of string
/// <summary>An empty permalink</summary>
static member Empty = Permalink ""
/// <inheritdoc />
override this.ToString() =
match this with Permalink it -> it
/// <summary>An identifier for a page</summary>
[<Struct>]
type PageId =
@@ -583,6 +570,19 @@ type PageId =
match this with PageId it -> it
/// <summary>A permanent link</summary>
[<Struct>]
type Permalink =
| Permalink of string
/// <summary>An empty permalink</summary>
static member Empty = Permalink ""
/// <inheritdoc />
override this.ToString() =
match this with Permalink it -> it
/// <summary>Statuses for posts</summary>
[<Struct>]
type PostStatus =
@@ -683,82 +683,6 @@ type CustomFeedSource =
match this with | Category (CategoryId catId) -> $"category:{catId}" | Tag tag -> $"tag:{tag}"
/// <summary>Options for a feed that describes a podcast</summary>
[<CLIMutable; NoComparison; NoEquality>]
type PodcastOptions = {
/// <summary>The title of the podcast</summary>
Title: string
/// <summary>A subtitle for the podcast</summary>
Subtitle: string option
/// <summary>The number of items in the podcast feed</summary>
ItemsInFeed: int
/// <summary>A summary of the podcast (iTunes field)</summary>
Summary: string
/// <summary>The display name of the podcast author (iTunes field)</summary>
DisplayedAuthor: string
/// <summary>The e-mail address of the user who registered the podcast at iTunes</summary>
Email: string
/// <summary>The link to the image for the podcast</summary>
ImageUrl: Permalink
/// <summary>The category from Apple Podcasts (iTunes) under which this podcast is categorized</summary>
AppleCategory: string
/// <summary>
/// A further refinement of the categorization of this podcast (Apple Podcasts/iTunes field / values)
/// </summary>
AppleSubcategory: string option
/// <summary>The explictness rating (iTunes field)</summary>
Explicit: ExplicitRating
/// <summary>The default media type for files in this podcast</summary>
DefaultMediaType: string option
/// <summary>
/// The base URL for relative URL media files for this podcast (optional; defaults to web log base)
/// </summary>
MediaBaseUrl: string option
/// <summary>A GUID for this podcast</summary>
PodcastGuid: Guid option
/// <summary>A URL at which information on supporting the podcast may be found (supports permalinks)</summary>
FundingUrl: string option
/// <summary>The text to be displayed in the funding item within the feed</summary>
FundingText: string option
/// <summary>The medium (what the podcast IS, not what it is ABOUT)</summary>
Medium: PodcastMedium option
} with
/// <summary>A default set of podcast options</summary>
static member Empty =
{ Title = ""
Subtitle = None
ItemsInFeed = 0
Summary = ""
DisplayedAuthor = ""
Email = ""
ImageUrl = Permalink.Empty
AppleCategory = ""
AppleSubcategory = None
Explicit = No
DefaultMediaType = None
MediaBaseUrl = None
PodcastGuid = None
FundingUrl = None
FundingText = None
Medium = None }
/// <summary>A custom feed</summary>
[<CLIMutable; NoComparison; NoEquality>]
type CustomFeed = {
+4 -2
View File
@@ -684,7 +684,7 @@ type EditCustomFeedModel = {
Summary = this.Summary
DisplayedAuthor = this.DisplayedAuthor
Email = this.Email
ImageUrl = Permalink this.ImageUrl
ImageUrl = this.ImageUrl
AppleCategory = this.AppleCategory
AppleSubcategory = noneIfBlank this.AppleSubcategory
Explicit = ExplicitRating.Parse this.Explicit
@@ -693,7 +693,8 @@ type EditCustomFeedModel = {
PodcastGuid = noneIfBlank this.PodcastGuid |> Option.map Guid.Parse
FundingUrl = noneIfBlank this.FundingUrl
FundingText = noneIfBlank this.FundingText
Medium = noneIfBlank this.Medium |> Option.map PodcastMedium.Parse }
Medium = noneIfBlank this.Medium |> Option.map PodcastMedium.Parse
People = None } // TODO: add this to UI or handle differently
else
None }
@@ -974,6 +975,7 @@ type EditPostModel() =
| Some it -> Some (double it)
| None -> None
EpisodeDescription = noneIfBlank this.EpisodeDescription
People = None // TODO: add this to UI or handle differently
}
else
None }