module RedCat.DrugsDataExtractor open System.Collections.Generic open Newtonsoft.Json.Linq open FuzzySharp open System.IO let drugsJson = File.ReadAllText(Path.Combine("/opt/redcatbot/", "drugs.json")) let comboJson = File.ReadAllText(Path.Combine("/opt/redcatbot/", "combos.json")) let DrugsJsonData = JObject.Parse(drugsJson) let ComboJsonData = JObject.Parse(drugsJson) let private tryExtractProps (propName: string) (jsonValue: JToken) = match jsonValue["properties"][propName] with | null -> None | token -> token.ToString() |> Some let private tryExtract (x: string) (jsonValue: JToken) = match jsonValue[x] with | null -> None | token -> token.ToString() |> Some let private tryExtractArray (fieldName: string) (jsonValue: JToken) = match jsonValue[fieldName] with | null -> None | categories -> categories.Values() |> Seq.cast |> Seq.map(fun text -> text.ToString()) |> Seq.toList |> Some let private DrugNamesWithAliases = DrugsJsonData.Properties() |> Seq.map(fun obj -> match obj.Value |> tryExtractArray "aliases" with | Some aliasesList -> aliasesList |> List.map(fun al -> (al, obj.Name)) | None -> [(obj.Name, obj.Name)]) |> Seq.concat |> Map.ofSeq let private DrugNames = DrugNamesWithAliases |> Map.values |> Set.ofSeq let private search (userInput: string) = let clearString (inStr: string) = inStr.Replace("-", "").Replace(",", "") let findCond (_in: KeyValuePair) = match (_in.Key, _in.Value) with | _, drugName when drugName = userInput || clearString drugName = userInput -> drugName |> Some | someAlias, drugName when someAlias = userInput -> drugName |> Some | _ -> None let drugNameFound = DrugNamesWithAliases |> Seq.choose(findCond) |> Seq.tryHead drugNameFound type DrugRecord = { Name: string Summary: string option Categories: string list option OnSet: string option TotalDuration: string option Dose: string option } type ComboRecord = { DrugA: string DrugB: string Status: string option Note: string option } type SearchResult = | DrugRecord of DrugRecord | ResponseText of string let getComboRecord (userInputA: string) (userInputB: string) = let getRecord (jsonValue: JToken) = { DrugA = userInputA DrugB = userInputB Status = jsonValue |> tryExtract "status" Note = jsonValue |> tryExtract "note" } let tryGetCombo drugA drugB = match DrugsJsonData[drugA][drugB] with | null -> None | obj -> Some obj match (search userInputA, search userInputB) with | None, None -> Error $"Can't find both drugs {userInputA} and {userInputB}" | Some _, None -> Error $"Can't find second drug {userInputB}" | None, Some _ -> Error $"Can't find first drug {userInputA}" | Some drugA, Some drugB -> tryGetCombo drugA drugB |> Option.bind(fun jsonValue -> (getRecord jsonValue) |> Some) |> function | None -> Error $"Can't find combo from {userInputA} and {userInputB}" | Some combo -> combo |> Ok let getSearchResponse (userInput: string) = let matches = Process.ExtractTop(userInput, DrugNames) |> Seq.choose(fun e -> if e.Score > 62 then e.Value |> Some else None) |> String.concat "\n- " match matches with | "" -> Error $"Can't find drug name: {userInput}" | matches -> $"- {matches}" |> ResponseText |> Ok let getDrugRecord (userInput: string) = let getRecord (drugName: string) (jsonValue: JToken) = { Name = drugName Summary = jsonValue |> tryExtractProps "summary" Categories = jsonValue |> tryExtractArray "categories" OnSet = jsonValue |> tryExtractProps "onset" TotalDuration = jsonValue |> tryExtractProps "duration" Dose = jsonValue |> tryExtractProps "dose" } match search userInput with | Some drugName -> getRecord drugName DrugsJsonData[drugName] |> DrugRecord |> Ok | None -> getSearchResponse userInput