module Harmonic.Evaluation.Database.Query
(
ComposerWeights
, parseComposerWeights
, normalizeWeights
, parseWeightsJson
, fetchTransitions
, fetchTransitionsAggregate
, ChangeCandidate(..)
, fetchChangeTransitions
, fetchChangeAggregate
, fetchChangeStarts
, resolveChangeCue
, fetchJazzComposers
, splitSeekByCorpus
, resolveWeights
, applyComposerBlend
) where
import qualified Data.Aeson as A
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import qualified Data.Text as T
import Data.Text (Text)
import qualified Data.Text.Encoding as TE
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Either (partitionEithers)
import Data.List (sortBy)
import Data.Ord (Down(..))
import Data.Function (on)
import qualified Data.Aeson as Aeson
import Data.Aeson (Value(..))
import qualified Data.Aeson.Key as Key
import qualified Data.Aeson.KeyMap as KM
import qualified Data.ByteString.Lazy as BL
import Data.Scientific (toRealFloat)
import qualified Data.Set as Set
import Harmonic.Database (DbActionT, runQuery, runQueryP)
import Text.Read (readMaybe)
import qualified Harmonic.Rules.Types.Harmony as H
type ComposerWeights = Map Text Double
parseComposerWeights :: Text -> ComposerWeights
parseComposerWeights :: Text -> ComposerWeights
parseComposerWeights Text
input
| Text -> Text
T.strip Text
input Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"*" = ComposerWeights
forall k a. Map k a
Map.empty
| Bool
otherwise =
let tokens :: [Text]
tokens = (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> Text -> [Text]
T.split Char -> Bool
isSeparator Text
input
parsed :: [(Text, Double)]
parsed = (Text -> Maybe (Text, Double)) -> [Text] -> [(Text, Double)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Text -> Maybe (Text, Double)
parseToken [Text]
tokens
in ComposerWeights -> ComposerWeights
normalizeWeights (ComposerWeights -> ComposerWeights)
-> ComposerWeights -> ComposerWeights
forall a b. (a -> b) -> a -> b
$ (Double -> Double -> Double) -> [(Text, Double)] -> ComposerWeights
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(+) [(Text, Double)]
parsed
where
isSeparator :: Char -> Bool
isSeparator Char
c = Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
','
parseToken :: Text -> Maybe (Text, Double)
parseToken :: Text -> Maybe (Text, Double)
parseToken Text
tok =
case HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
":" Text
tok of
[Text
name] -> (Text, Double) -> Maybe (Text, Double)
forall a. a -> Maybe a
Just (Text -> Text
T.toLower (Text -> Text
T.strip Text
name), Double
1.0)
[Text
name, Text
wStr] ->
let weight :: Double
weight = Text -> Double
parseWeight (Text -> Text
T.strip Text
wStr)
in (Text, Double) -> Maybe (Text, Double)
forall a. a -> Maybe a
Just (Text -> Text
T.toLower (Text -> Text
T.strip Text
name), Double
weight)
[Text]
_ -> Maybe (Text, Double)
forall a. Maybe a
Nothing
parseWeight :: Text -> Double
parseWeight :: Text -> Double
parseWeight Text
wStr =
let str :: String
str = Text -> String
T.unpack Text
wStr
in case ReadS Double
forall a. Read a => ReadS a
reads String
str of
[(Double
d, String
"")] -> Double
d
[(Double, String)]
_ -> Double
1.0
normalizeWeights :: ComposerWeights -> ComposerWeights
normalizeWeights :: ComposerWeights -> ComposerWeights
normalizeWeights ComposerWeights
weights
| Double
total Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
0 = ComposerWeights
weights
| Bool
otherwise = (Double -> Double) -> ComposerWeights -> ComposerWeights
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map (Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
total) ComposerWeights
weights
where
total :: Double
total = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (ComposerWeights -> [Double]
forall k a. Map k a -> [a]
Map.elems ComposerWeights
weights)
fetchTransitions :: Text -> DbActionT [(H.Cadence, ComposerWeights)]
fetchTransitions :: Text -> DbActionT [(Cadence, ComposerWeights)]
fetchTransitions Text
cadenceShow = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (c:Cadence {show: $show})-[r:NEXT]->(n:Cadence)"
, Text
"RETURN n.movement AS movement, n.chord AS chord, r.weights AS weights"
]
params :: Map Text Value
params = [(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text
"show", Text -> Value
A.String Text
cadenceShow)]
records <- Text -> Map Text Value -> DbActionT [Map Text Value]
runQueryP Text
query Map Text Value
params
pure $ mapMaybe parseRecord records
where
parseRecord :: Map Text A.Value -> Maybe (H.Cadence, ComposerWeights)
parseRecord :: Map Text Value -> Maybe (Cadence, ComposerWeights)
parseRecord Map Text Value
record = do
mvmtVal <- Text -> Map Text Value -> Maybe Value
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
"movement" Map Text Value
record
chordVal <- Map.lookup "chord" record
weightsVal <- Map.lookup "weights" record
mvmtStr <- extractText mvmtVal
chordStr <- extractText chordVal
weightsStr <- extractText weightsVal
let cadence = (String, String) -> Cadence
H.constructCadence (Text -> String
T.unpack Text
mvmtStr, Text -> String
T.unpack Text
chordStr)
let weights = Text -> ComposerWeights
parseWeightsJson Text
weightsStr
pure (cadence, weights)
fetchTransitionsAggregate :: Text -> DbActionT [(H.Cadence, Double)]
fetchTransitionsAggregate :: Text -> DbActionT [(Cadence, Double)]
fetchTransitionsAggregate Text
cadenceShow = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (c:Cadence {show: $show})-[r:NEXT]->(n:Cadence)"
, Text
"RETURN n.movement AS movement, n.chord AS chord, r.confidence AS confidence"
]
params :: Map Text Value
params = [(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text
"show", Text -> Value
A.String Text
cadenceShow)]
records <- Text -> Map Text Value -> DbActionT [Map Text Value]
runQueryP Text
query Map Text Value
params
pure $ sortBy (compare `on` (Down . snd))
$ filter ((> 0) . snd)
$ mapMaybe parseRecord records
where
parseRecord :: Map Text A.Value -> Maybe (H.Cadence, Double)
parseRecord :: Map Text Value -> Maybe (Cadence, Double)
parseRecord Map Text Value
record = do
mvmtVal <- Text -> Map Text Value -> Maybe Value
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
"movement" Map Text Value
record
chordVal <- Map.lookup "chord" record
confVal <- Map.lookup "confidence" record
mvmtStr <- extractText mvmtVal
chordStr <- extractText chordVal
conf <- extractDouble confVal
let cadence = (String, String) -> Cadence
H.constructCadence (Text -> String
T.unpack Text
mvmtStr, Text -> String
T.unpack Text
chordStr)
pure (cadence, conf)
resolveWeights :: ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
resolveWeights :: forall a.
ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
resolveWeights ComposerWeights
blend [(a, ComposerWeights)]
candidates =
let scored :: [(a, Double)]
scored = ((a, ComposerWeights) -> (a, Double))
-> [(a, ComposerWeights)] -> [(a, Double)]
forall a b. (a -> b) -> [a] -> [b]
map (ComposerWeights -> (a, ComposerWeights) -> (a, Double)
forall a. ComposerWeights -> (a, ComposerWeights) -> (a, Double)
scoreCandidate ComposerWeights
blend) [(a, ComposerWeights)]
candidates
sorted :: [(a, Double)]
sorted = ((a, Double) -> (a, Double) -> Ordering)
-> [(a, Double)] -> [(a, Double)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (Down Double -> Down Double -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Down Double -> Down Double -> Ordering)
-> ((a, Double) -> Down Double)
-> (a, Double)
-> (a, Double)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Double -> Down Double
forall a. a -> Down a
Down (Double -> Down Double)
-> ((a, Double) -> Double) -> (a, Double) -> Down Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, Double) -> Double
forall a b. (a, b) -> b
snd)) [(a, Double)]
scored
in [(a, Double)]
sorted
where
scoreCandidate :: ComposerWeights -> (a, ComposerWeights) -> (a, Double)
scoreCandidate :: forall a. ComposerWeights -> (a, ComposerWeights) -> (a, Double)
scoreCandidate ComposerWeights
userBlend (a
cadence, ComposerWeights
edgeWeights)
| ComposerWeights -> Bool
forall k a. Map k a -> Bool
Map.null ComposerWeights
userBlend =
(a
cadence, [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (ComposerWeights -> [Double]
forall k a. Map k a -> [a]
Map.elems ComposerWeights
edgeWeights))
| Bool
otherwise =
let edgeLower :: ComposerWeights
edgeLower = (Double -> Double -> Double) -> [(Text, Double)] -> ComposerWeights
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(+)
[ (Text -> Text
T.toLower Text
k, Double
v) | (Text
k, Double
v) <- ComposerWeights -> [(Text, Double)]
forall k a. Map k a -> [(k, a)]
Map.toList ComposerWeights
edgeWeights ]
score :: Double
score = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum
[ Double
userWeight Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double -> Maybe Double -> Double
forall a. a -> Maybe a -> a
fromMaybe Double
0 (Text -> ComposerWeights -> Maybe Double
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (Text -> Text
T.toLower Text
composer) ComposerWeights
edgeLower)
| (Text
composer, Double
userWeight) <- ComposerWeights -> [(Text, Double)]
forall k a. Map k a -> [(k, a)]
Map.toList ComposerWeights
userBlend
]
in (a
cadence, Double
score)
applyComposerBlend :: ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
applyComposerBlend :: forall a.
ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
applyComposerBlend ComposerWeights
blend = ((a, Double) -> Bool) -> [(a, Double)] -> [(a, Double)]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> Double
0) (Double -> Bool) -> ((a, Double) -> Double) -> (a, Double) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, Double) -> Double
forall a b. (a, b) -> b
snd) ([(a, Double)] -> [(a, Double)])
-> ([(a, ComposerWeights)] -> [(a, Double)])
-> [(a, ComposerWeights)]
-> [(a, Double)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
forall a.
ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
resolveWeights ComposerWeights
blend
parseWeightsJson :: Text -> ComposerWeights
parseWeightsJson :: Text -> ComposerWeights
parseWeightsJson Text
jsonStr =
case ByteString -> Maybe Value
forall a. FromJSON a => ByteString -> Maybe a
Aeson.decode (StrictByteString -> ByteString
BL.fromStrict (StrictByteString -> ByteString) -> StrictByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> StrictByteString
TE.encodeUtf8 Text
jsonStr) of
Just (Object Object
obj) ->
[(Text, Double)] -> ComposerWeights
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Key -> Text
Key.toText Key
k, Value -> Double
forall {b}. Fractional b => Value -> b
extractNum Value
v) | (Key
k, Value
v) <- Object -> [(Key, Value)]
forall v. KeyMap v -> [(Key, v)]
KM.toList Object
obj]
Maybe Value
_ -> ComposerWeights
forall k a. Map k a
Map.empty
where
extractNum :: Value -> b
extractNum (Number Scientific
n) = Scientific -> b
forall a b. (Real a, Fractional b) => a -> b
realToFrac Scientific
n
extractNum Value
_ = b
0
extractText :: A.Value -> Maybe Text
(A.String Text
t) = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
t
extractText Value
_ = Maybe Text
forall a. Maybe a
Nothing
extractDouble :: A.Value -> Maybe Double
(A.Number Scientific
n) = Double -> Maybe Double
forall a. a -> Maybe a
Just (Scientific -> Double
forall a. RealFloat a => Scientific -> a
toRealFloat Scientific
n)
extractDouble Value
_ = Maybe Double
forall a. Maybe a
Nothing
data ChangeCandidate = ChangeCandidate
{ ChangeCandidate -> Text
ccShow :: Text
, ChangeCandidate -> Movement
ccMovement :: H.Movement
, ChangeCandidate -> [Int]
ccSet :: [Int]
} deriving (Int -> ChangeCandidate -> String -> String
[ChangeCandidate] -> String -> String
ChangeCandidate -> String
(Int -> ChangeCandidate -> String -> String)
-> (ChangeCandidate -> String)
-> ([ChangeCandidate] -> String -> String)
-> Show ChangeCandidate
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> ChangeCandidate -> String -> String
showsPrec :: Int -> ChangeCandidate -> String -> String
$cshow :: ChangeCandidate -> String
show :: ChangeCandidate -> String
$cshowList :: [ChangeCandidate] -> String -> String
showList :: [ChangeCandidate] -> String -> String
Show, ChangeCandidate -> ChangeCandidate -> Bool
(ChangeCandidate -> ChangeCandidate -> Bool)
-> (ChangeCandidate -> ChangeCandidate -> Bool)
-> Eq ChangeCandidate
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ChangeCandidate -> ChangeCandidate -> Bool
== :: ChangeCandidate -> ChangeCandidate -> Bool
$c/= :: ChangeCandidate -> ChangeCandidate -> Bool
/= :: ChangeCandidate -> ChangeCandidate -> Bool
Eq)
fetchChangeTransitions :: Text -> DbActionT [(ChangeCandidate, ComposerWeights)]
fetchChangeTransitions :: Text -> DbActionT [(ChangeCandidate, ComposerWeights)]
fetchChangeTransitions Text
showKey = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (c:Change {show: $show})-[r:NEXT]->(n:Change)"
, Text
"RETURN n.show AS show, n.movement AS movement, n.chord AS chord, r.weights AS weights"
]
records <- Text -> Map Text Value -> DbActionT [Map Text Value]
runQueryP Text
query ([(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text
"show", Text -> Value
A.String Text
showKey)])
pure $ mapMaybe parseRecord records
where
parseRecord :: Map Text Value -> Maybe (ChangeCandidate, ComposerWeights)
parseRecord Map Text Value
record = do
cand <- Map Text Value -> Maybe ChangeCandidate
parseCandidate Map Text Value
record
weightsStr <- extractText =<< Map.lookup "weights" record
pure (cand, parseWeightsJson weightsStr)
fetchChangeAggregate :: Text -> DbActionT [(ChangeCandidate, Double)]
fetchChangeAggregate :: Text -> DbActionT [(ChangeCandidate, Double)]
fetchChangeAggregate Text
showKey = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (c:Change {show: $show})-[r:NEXT]->(n:Change)"
, Text
"RETURN n.show AS show, n.movement AS movement, n.chord AS chord, r.confidence AS confidence"
]
records <- Text -> Map Text Value -> DbActionT [Map Text Value]
runQueryP Text
query ([(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text
"show", Text -> Value
A.String Text
showKey)])
pure $ sortBy (compare `on` (Down . snd))
$ filter ((> 0) . snd)
$ mapMaybe parseRecord records
where
parseRecord :: Map Text Value -> Maybe (ChangeCandidate, Double)
parseRecord Map Text Value
record = do
cand <- Map Text Value -> Maybe ChangeCandidate
parseCandidate Map Text Value
record
conf <- extractDouble =<< Map.lookup "confidence" record
pure (cand, conf)
parseCandidate :: Map Text A.Value -> Maybe ChangeCandidate
parseCandidate :: Map Text Value -> Maybe ChangeCandidate
parseCandidate Map Text Value
record = do
showTxt <- Value -> Maybe Text
extractText (Value -> Maybe Text) -> Maybe Value -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Text -> Map Text Value -> Maybe Value
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
"show" Map Text Value
record
mvmtStr <- extractText =<< Map.lookup "movement" record
chordStr <- extractText =<< Map.lookup "chord" record
mvmt <- readMaybe (T.unpack mvmtStr)
set <- readMaybe (T.unpack chordStr)
pure (ChangeCandidate showTxt mvmt set)
fetchChangeStarts :: DbActionT [(ChangeCandidate, Double)]
fetchChangeStarts :: DbActionT [(ChangeCandidate, Double)]
fetchChangeStarts = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (c:Change)-[r:NEXT]->(:Change)"
, Text
"WITH c, sum(r.confidence) AS out"
, Text
"RETURN c.show AS show, c.movement AS movement, c.chord AS chord, out AS out"
]
records <- Text -> DbActionT [Map Text Value]
runQuery Text
query
pure $ filter ((> 0) . snd) $ mapMaybe parseRow records
where
parseRow :: Map Text Value -> Maybe (ChangeCandidate, Double)
parseRow Map Text Value
record = do
cand <- Map Text Value -> Maybe ChangeCandidate
parseCandidate Map Text Value
record
out <- extractDouble =<< Map.lookup "out" record
pure (cand, out)
resolveChangeCue :: Text -> DbActionT (Maybe Text)
resolveChangeCue :: Text -> DbActionT (Maybe Text)
resolveChangeCue Text
functionality = do
let query :: Text
query = [Text] -> Text
T.unlines
[ Text
"MATCH (n:Change) WHERE n.show ENDS WITH $suffix"
, Text
"OPTIONAL MATCH (n)-[r:NEXT]->(:Change)"
, Text
"WITH n.show AS s, sum(r.confidence) AS out"
, Text
"RETURN s ORDER BY (CASE WHEN s STARTS WITH '( pedal' THEN 1 ELSE 0 END) DESC, out DESC"
, Text
"LIMIT 1"
]
suffix :: Text
suffix = Text
"-> " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
functionality Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" )"
records <- Text -> Map Text Value -> DbActionT [Map Text Value]
runQueryP Text
query ([(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text
"suffix", Text -> Value
A.String Text
suffix)])
pure $ case records of
(Map Text Value
r:[Map Text Value]
_) -> Value -> Maybe Text
extractText (Value -> Maybe Text) -> Maybe Value -> Maybe Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Text -> Map Text Value -> Maybe Value
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Text
"s" Map Text Value
r
[] -> Maybe Text
forall a. Maybe a
Nothing
splitSeekByCorpus :: Set.Set Text -> ComposerWeights -> (ComposerWeights, ComposerWeights)
splitSeekByCorpus :: Set Text -> ComposerWeights -> (ComposerWeights, ComposerWeights)
splitSeekByCorpus Set Text
jazzKeys ComposerWeights
blend =
let expand :: (Text, b) -> Either [(Text, b)] (Text, b)
expand (Text
tok, b
w)
| Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Text
tok Set Text
jazzKeys = [(Text, b)] -> Either [(Text, b)] (Text, b)
forall a b. a -> Either a b
Left [(Text
tok, b
w)]
| Bool -> Bool
not ([Text] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
subs) = [(Text, b)] -> Either [(Text, b)] (Text, b)
forall a b. a -> Either a b
Left [ (Text
k, b
w b -> b -> b
forall a. Fractional a => a -> a -> a
/ Int -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
subs)) | Text
k <- [Text]
subs ]
| Bool
otherwise = (Text, b) -> Either [(Text, b)] (Text, b)
forall a b. b -> Either a b
Right (Text
tok, b
w)
where subs :: [Text]
subs = [ Text
k | Text
k <- Set Text -> [Text]
forall a. Set a -> [a]
Set.toList Set Text
jazzKeys, Text
tok Text -> Text -> Bool
`T.isInfixOf` Text
k ]
([[(Text, Double)]]
js, [(Text, Double)]
cs) = [Either [(Text, Double)] (Text, Double)]
-> ([[(Text, Double)]], [(Text, Double)])
forall a b. [Either a b] -> ([a], [b])
partitionEithers (((Text, Double) -> Either [(Text, Double)] (Text, Double))
-> [(Text, Double)] -> [Either [(Text, Double)] (Text, Double)]
forall a b. (a -> b) -> [a] -> [b]
map (Text, Double) -> Either [(Text, Double)] (Text, Double)
forall {b}.
Fractional b =>
(Text, b) -> Either [(Text, b)] (Text, b)
expand (ComposerWeights -> [(Text, Double)]
forall k a. Map k a -> [(k, a)]
Map.toList ComposerWeights
blend))
in ( ComposerWeights -> ComposerWeights
normalizeWeights ((Double -> Double -> Double) -> [(Text, Double)] -> ComposerWeights
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith Double -> Double -> Double
forall a. Num a => a -> a -> a
(+) ([[(Text, Double)]] -> [(Text, Double)]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[(Text, Double)]]
js))
, ComposerWeights -> ComposerWeights
normalizeWeights ([(Text, Double)] -> ComposerWeights
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text, Double)]
cs) )
fetchJazzComposers :: DbActionT (Set.Set Text)
fetchJazzComposers :: DbActionT (Set Text)
fetchJazzComposers = do
records <- Text -> DbActionT [Map Text Value]
runQuery Text
"MATCH (:Change)-[r:NEXT]->(:Change) RETURN r.weights AS w"
pure $ Set.fromList
[ T.toLower k
| record <- records
, Just wtxt <- [extractText =<< Map.lookup "w" record]
, k <- Map.keys (parseWeightsJson wtxt)
]