wikimusic-model-hs-1.2.0.0: src/WikiMusic/Model/Song.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoFieldSelectors #-}
module WikiMusic.Model.Song
( Song (..),
SongArtwork (..),
SongComment (..),
SongOpinion (..),
SongContent (..),
validateSong,
validateSongArtwork,
validateSongComment,
validateSongOpinion,
validateSongContent,
SongSortOrder (..),
validateArtistOfSong,
ArtistOfSong (..),
SongExternalSources (..),
validateSongExternalSources,
SongArtworkOrderUpdate (..),
validateSongArtworkOrderUpdate,
validateSongDelta,
SongDelta (..),
validateSongContentDelta,
SongContentDelta (..),
Prelude.show,
Prelude.read,
SongIncludes (..),
EnrichSongParams (..),
fullEnrichment,
noEnrichment,
parseInclude,
)
where
import Data.Aeson hiding (Success)
import Data.Map qualified as Map
import Data.OpenApi
import Data.Text qualified as T
import Data.Time
import Data.UUID
import Keuringsdienst
import Keuringsdienst.Helpers
import Optics
import Relude
import Text.Read
import WikiMusic.Model.Artwork
import WikiMusic.Model.Comment
import WikiMusic.Model.Opinion
import WikiMusic.Model.Thread
import Prelude qualified
data SongSortOrder
= DescCreatedAt
| AscCreatedAt
| DescLastEditedAt
| AscLastEditedAt
| DescDisplayName
| AscDisplayName
instance Show SongSortOrder where
show DescCreatedAt = "songs.created_at DESC"
show AscCreatedAt = "songs.created_at ASC"
show DescLastEditedAt = "songs.last_edited_at DESC"
show AscLastEditedAt = "songs.last_edited_at ASC"
show DescDisplayName = "songs.display_name DESC"
show AscDisplayName = "songs.display_name ASC"
instance Read SongSortOrder where
readsPrec _ "created-at-desc" = [(DescCreatedAt, "")]
readsPrec _ "created-at-asc" = [(AscCreatedAt, "")]
readsPrec _ "last-edited-at-desc" = [(DescLastEditedAt, "")]
readsPrec _ "last-edited-at-asc" = [(AscLastEditedAt, "")]
readsPrec _ "display-name-desc" = [(DescDisplayName, "")]
readsPrec _ "display-name-asc" = [(AscDisplayName, "")]
readsPrec _ _ = []
data SongContent = SongContent
{ identifier :: UUID,
songIdentifier :: UUID,
versionName :: Text,
createdBy :: UUID,
visibilityStatus :: Int,
approvedBy :: Maybe UUID,
instrumentType :: Text,
asciiLegend :: Maybe Text,
asciiContents :: Maybe Text,
pdfContents :: Maybe Text,
guitarProContents :: Maybe Text,
createdAt :: UTCTime,
lastEditedAt :: Maybe UTCTime
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
makeFieldLabelsNoPrefix ''SongContent
data SongOpinion = SongOpinion
{ songIdentifier :: UUID,
opinion :: Opinion
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
validateSongArtwork :: SongArtwork -> ValidationResult
validateSongArtwork x =
(x ^. #artwork % #contentUrl)
|?| isNonEmptyText
<> (x ^. #artwork % #contentCaption)
|??| isNonEmptyText
<> (x ^. #artwork % #visibilityStatus)
|?| isPositiveOrZero
<> (x ^. #artwork % #orderValue)
|?| isPositiveOrZero
data SongArtwork = SongArtwork
{ songIdentifier :: UUID,
artwork :: Artwork
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
validateSongComment :: SongComment -> ValidationResult
validateSongComment x =
(x ^. #comment % #visibilityStatus)
|?| isPositiveOrZero
<> (x ^. #comment % #contents)
|?| (isNonEmptyText <> isTextSmallerThanOrEqual 8200)
data SongComment = SongComment
{ songIdentifier :: UUID,
comment :: Comment
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
validateSongExternalSources :: SongExternalSources -> ValidationResult
validateSongExternalSources x =
(x ^. #spotifyUrl)
|??| isNonEmptyText
<> (x ^. #youtubeUrl)
|??| isNonEmptyText
<> (x ^. #soundcloudUrl)
|??| isNonEmptyText
<> (x ^. #wikipediaUrl)
|??| isNonEmptyText
data SongExternalSources = SongExternalSources
{ identifier :: UUID,
songIdentifier :: UUID,
createdBy :: UUID,
spotifyUrl :: Maybe Text,
youtubeUrl :: Maybe Text,
soundcloudUrl :: Maybe Text,
wikipediaUrl :: Maybe Text,
createdAt :: UTCTime,
lastEditedAt :: Maybe UTCTime
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
makeFieldLabelsNoPrefix ''SongExternalSources
data Song = Song
{ identifier :: UUID,
displayName :: Text,
musicKey :: Maybe Text,
musicTuning :: Maybe Text,
musicCreationDate :: Maybe Text,
albumName :: Maybe Text,
albumInfoLink :: Maybe Text,
createdBy :: UUID,
visibilityStatus :: Int,
approvedBy :: Maybe UUID,
createdAt :: UTCTime,
lastEditedAt :: Maybe UTCTime,
artworks :: Map UUID SongArtwork,
comments :: [ThreadRender SongComment],
opinions :: Map UUID SongOpinion,
contents :: Map UUID SongContent,
spotifyUrl :: Maybe Text,
youtubeUrl :: Maybe Text,
soundcloudUrl :: Maybe Text,
wikipediaUrl :: Maybe Text,
artists :: Map UUID Text,
viewCount :: Int,
description :: Maybe Text
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
data SongArtworkOrderUpdate = SongArtworkOrderUpdate
{ identifier :: UUID,
orderValue :: Int
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
data EnrichSongParams = EnrichSongParams
{ includeComments :: Bool,
includeOpinions :: Bool,
includeArtworks :: Bool,
includeArtists :: Bool,
includeContents :: Bool
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
makeFieldLabelsNoPrefix ''EnrichSongParams
data SongIncludes
= IncludeArtists
| IncludeComments
| IncludeOpinions
| IncludeArtworks
| IncludeContents
deriving (Eq, Generic, FromJSON, ToJSON, ToSchema)
instance Show SongIncludes where
show IncludeComments = "comments"
show IncludeOpinions = "opinions"
show IncludeArtworks = "artworks"
show IncludeArtists = "artists"
show IncludeContents = "contents"
instance Read SongIncludes where
readsPrec _ "comments" = [(IncludeComments, "")]
readsPrec _ "opinions" = [(IncludeOpinions, "")]
readsPrec _ "artworks" = [(IncludeArtworks, "")]
readsPrec _ "artists" = [(IncludeArtists, "")]
readsPrec _ "contents" = [(IncludeContents, "")]
readsPrec _ _ = []
fullEnrichment :: EnrichSongParams
fullEnrichment =
EnrichSongParams
{ includeComments = True,
includeArtworks = True,
includeOpinions = True,
includeArtists = True,
includeContents = True
}
noEnrichment :: EnrichSongParams
noEnrichment =
EnrichSongParams
{ includeComments = False,
includeArtworks = False,
includeOpinions = False,
includeArtists = False,
includeContents = False
}
parseInclude :: Text -> EnrichSongParams
parseInclude includeString = do
EnrichSongParams
{ includeComments = fromIncludeMap IncludeComments,
includeOpinions = fromIncludeMap IncludeOpinions,
includeArtworks = fromIncludeMap IncludeArtworks,
includeArtists = fromIncludeMap IncludeArtists,
includeContents = fromIncludeMap IncludeContents
}
where
includes = Map.fromList $ map (,True) (T.split (== ',') includeString)
fromIncludeMap k = fromMaybe False (includes Map.!? (T.pack . show $ k))
validateArtistOfSong :: ArtistOfSong -> ValidationResult
validateArtistOfSong _ = Success
data ArtistOfSong = ArtistOfSong
{ identifier :: UUID,
songIdentifier :: UUID,
artistIdentifier :: UUID,
createdAt :: UTCTime,
createdBy :: UUID
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
validateSongContent :: SongContent -> ValidationResult
validateSongContent x =
(x ^. #versionName)
|?| isTextSmallerThanOrEqual 400
<> (x ^. #instrumentType)
|?| isNonEmptyText
<> (x ^. #asciiLegend)
|??| isNonEmptyText
<> (x ^. #asciiContents)
|??| isNonEmptyText
validateSongContentDelta :: SongContentDelta -> ValidationResult
validateSongContentDelta x =
(x ^. #versionName)
|?| isTextSmallerThanOrEqual 400
<> (x ^. #instrumentType)
|??| isNonEmptyText
<> (x ^. #asciiLegend)
|??| isNonEmptyText
<> (x ^. #asciiContents)
|??| isNonEmptyText
data SongContentDelta = SongContentDelta
{ identifier :: UUID,
versionName :: Text,
instrumentType :: Maybe Text,
asciiLegend :: Maybe Text,
asciiContents :: Maybe Text,
pdfContents :: Maybe Text,
guitarProContents :: Maybe Text
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
makeFieldLabelsNoPrefix ''Song
makeFieldLabelsNoPrefix ''SongComment
makeFieldLabelsNoPrefix ''SongArtwork
makeFieldLabelsNoPrefix ''SongOpinion
makeFieldLabelsNoPrefix ''ArtistOfSong
data SongDelta = SongDelta
{ identifier :: UUID,
displayName :: Maybe Text,
musicKey :: Maybe Text,
musicTuning :: Maybe Text,
musicCreationDate :: Maybe Text,
albumName :: Maybe Text,
albumInfoLink :: Maybe Text,
spotifyUrl :: Maybe Text,
youtubeUrl :: Maybe Text,
soundcloudUrl :: Maybe Text,
wikipediaUrl :: Maybe Text,
description :: Maybe Text
}
deriving (Eq, Show, Generic, FromJSON, ToJSON, ToSchema)
makeFieldLabelsNoPrefix ''SongDelta
validateSongDelta :: SongDelta -> ValidationResult
validateSongDelta x =
(x ^. #displayName)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #musicKey)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 100)
<> (x ^. #musicTuning)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 100)
<> (x ^. #musicCreationDate)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #albumName)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #albumInfoLink)
|??| isNonEmptyText
<> (x ^. #spotifyUrl)
|??| isNonEmptyText
<> (x ^. #youtubeUrl)
|??| isNonEmptyText
<> (x ^. #soundcloudUrl)
|??| isNonEmptyText
<> (x ^. #wikipediaUrl)
|??| isNonEmptyText
validateSong :: Song -> ValidationResult
validateSong x =
(x ^. #displayName)
|?| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #musicKey)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 50)
<> (x ^. #musicTuning)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 50)
<> (x ^. #musicCreationDate)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #albumName)
|??| (isNonEmptyText <> isTextSmallerThanOrEqual 340)
<> (x ^. #albumInfoLink)
|??| isNonEmptyText
<> (x ^. #visibilityStatus)
|?| isPositiveOrZero
<> (x ^. #spotifyUrl)
|??| isNonEmptyText
<> (x ^. #youtubeUrl)
|??| isNonEmptyText
<> (x ^. #soundcloudUrl)
|??| isNonEmptyText
<> (x ^. #wikipediaUrl)
|??| isNonEmptyText
validateSongOpinion :: SongOpinion -> ValidationResult
validateSongOpinion x =
(x ^. #opinion % #isLike)
|?| isNotEqualTo (x ^. #opinion % #isDislike)
<> (x ^. #opinion % #isDislike)
|?| isNotEqualTo (x ^. #opinion % #isLike)
validateSongArtworkOrderUpdate :: SongArtworkOrderUpdate -> ValidationResult
validateSongArtworkOrderUpdate x =
(x ^. #orderValue) |?| isPositiveOrZero