From 00153681f063283c9ec728611f592cf511e291af Mon Sep 17 00:00:00 2001 From: MaheshPunna Date: Wed, 3 Nov 2021 21:36:59 +0530 Subject: [PATCH] Unfinished ChartStudio Integration --- Plotly.NET.sln | 9 + src/Plotly.NET.ChartStudio/API.fs | 89 ++++++++ src/Plotly.NET.ChartStudio/Chart.fs | 55 +++++ src/Plotly.NET.ChartStudio/Config.fs | 212 ++++++++++++++++++ .../Plotly.NET.ChartStudio.fsproj | 23 ++ src/Plotly.NET.ChartStudio/Program.fs | 17 ++ 6 files changed, 405 insertions(+) create mode 100644 src/Plotly.NET.ChartStudio/API.fs create mode 100644 src/Plotly.NET.ChartStudio/Chart.fs create mode 100644 src/Plotly.NET.ChartStudio/Config.fs create mode 100644 src/Plotly.NET.ChartStudio/Plotly.NET.ChartStudio.fsproj create mode 100644 src/Plotly.NET.ChartStudio/Program.fs diff --git a/Plotly.NET.sln b/Plotly.NET.sln index 91b2bb5b7..46ba748a3 100644 --- a/Plotly.NET.sln +++ b/Plotly.NET.sln @@ -133,6 +133,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.Tests.CSharpCons EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plotly.NET.Tests.CSharp", "tests\Plotly.NET.Tests.CSharp\Plotly.NET.Tests.CSharp.csproj", "{26539796-0C9D-4856-8584-B58BE32CC495}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Plotly.NET.ChartStudio", "src\Plotly.NET.ChartStudio\Plotly.NET.ChartStudio.fsproj", "{9574A266-2AC3-469C-83A6-D2EF72CECFD4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -182,6 +184,12 @@ Global {26539796-0C9D-4856-8584-B58BE32CC495}.Dotnet|Any CPU.Build.0 = Debug|Any CPU {26539796-0C9D-4856-8584-B58BE32CC495}.Release|Any CPU.ActiveCfg = Release|Any CPU {26539796-0C9D-4856-8584-B58BE32CC495}.Release|Any CPU.Build.0 = Release|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Dotnet|Any CPU.Build.0 = Debug|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9574A266-2AC3-469C-83A6-D2EF72CECFD4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,6 +205,7 @@ Global {60114ACE-77E6-4A19-9A2F-CB64084174AF} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} {1BC73DA0-586F-45C2-BC5B-A70C452A00F0} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} {26539796-0C9D-4856-8584-B58BE32CC495} = {EAE25A1F-86FC-426B-803F-1006D1AD06A8} + {9574A266-2AC3-469C-83A6-D2EF72CECFD4} = {0E87E47E-9EDC-4525-AF72-F0E139D54236} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7177F1E1-341C-48AB-9864-6B525FFF7633} diff --git a/src/Plotly.NET.ChartStudio/API.fs b/src/Plotly.NET.ChartStudio/API.fs new file mode 100644 index 000000000..9837f3250 --- /dev/null +++ b/src/Plotly.NET.ChartStudio/API.fs @@ -0,0 +1,89 @@ +namespace Plotly.NET.ChartStudio + +open DynamicObj +open System +open System.Runtime.InteropServices +open System.Net.Http +open System.Web + +type API() = + + static let RESOURCE = "plots" + + static member buildURL + ( + resource, + [] ?id: String, + [] ?route: String + ) = + + let config = Config.getConfigFile () + + let baseURL = + match config.TryGetValue "plotly_api_domain" with + | Some apiDomain -> string apiDomain + | _ -> "https://api.plotly.com" + + let urlWithID = + match id with + | Some x when (not (String.IsNullOrEmpty(x))) -> $"{baseURL}//v2//{resource}//{x}" + | _ -> $"{baseURL}//v2//{resource}" + + let url = + match route with + | Some x when (not (String.IsNullOrEmpty(x))) -> $"{urlWithID}//{route}" + | _ -> $"{urlWithID}" + + url + + static member create body = + async { + use client = new HttpClient() + + let url = API.buildURL RESOURCE + let content = new StringContent(body) + printf "%A" url + + let! response = client.PostAsync(url, content) |> Async.AwaitTask + + let! body = + response.Content.ReadAsStringAsync() + |> Async.AwaitTask + + return + match response.IsSuccessStatusCode with + | true -> Ok body + | false -> Error body + } + |> Async.RunSynchronously + + static member retrieve(fid, [] ?share_key: String) = + async { + use client = new HttpClient() + + let url = + API.buildURL (resource = RESOURCE, id = fid) + + let url = + match share_key with + | Some key -> + let query = + HttpUtility.ParseQueryString(String.Empty) + + query.Add("share_key", key) + + let builder = + new UriBuilder(url, Query = query.ToString()) + + builder.Uri.ToString() + | _ -> url + + let! response = client.GetAsync(url) |> Async.AwaitTask + + let! content = + response.Content.ReadAsStringAsync() + |> Async.AwaitTask + + return content + } + |> Async.RunSynchronously diff --git a/src/Plotly.NET.ChartStudio/Chart.fs b/src/Plotly.NET.ChartStudio/Chart.fs new file mode 100644 index 000000000..456f4b04c --- /dev/null +++ b/src/Plotly.NET.ChartStudio/Chart.fs @@ -0,0 +1,55 @@ +namespace Plotly.NET.ChartStudio + +open Plotly.NET +open Plotly.NET.GenericChart +open System.Runtime.InteropServices +open Newtonsoft.Json +open System.Collections + +/// Extensions methods from Plotly.NET.ImageExport for the Chart module, supporting the fluent pipeline style +[] +module ChartExtensions = + let internal jsonConfig = JsonSerializerSettings() + jsonConfig.ReferenceLoopHandling <- ReferenceLoopHandling.Serialize + + type Chart with + + [] + static member postToCloud + ( + [] ?FileName: string, + [] ?AutoOpen: bool, + [] ?Sharing: string + ) = + + let extractGridFromFigure (gChart: GenericChart) = + let traces = getTraces gChart + + let dataArrays = + [ for trace in traces -> + trace.GetProperties(true) + |> Seq.filter (fun x -> (x.Value :? IEnumerable && not (x.Value :? string))) ] + + |> Seq.mapi + (fun i data -> + data + |> Seq.map + (fun kvp -> + {| Name = $"data.{i}.{kvp.Key}" + Data = kvp.Value |})) + + printf "%A" dataArrays + () + + + + fun (gChart: GenericChart) -> + + extractGridFromFigure gChart + + let figure = GenericChart.toFigure gChart + + let payload = + JsonConvert.SerializeObject({| figure = figure |}, jsonConfig) + + API.create payload diff --git a/src/Plotly.NET.ChartStudio/Config.fs b/src/Plotly.NET.ChartStudio/Config.fs new file mode 100644 index 000000000..a60afc30a --- /dev/null +++ b/src/Plotly.NET.ChartStudio/Config.fs @@ -0,0 +1,212 @@ +namespace Plotly.NET.ChartStudio + +open DynamicObj +open System +open System.Runtime.InteropServices +open System.IO +open Newtonsoft.Json.Linq +open Newtonsoft.Json +open System.Diagnostics + +type Credentials() = + inherit DynamicObj() + + static let PLOTLY_DIR = + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + + static let CREDENTIALS_FILE = Path.Combine(PLOTLY_DIR, ".credentials") + + static member init + ( + [] ?Username: string, + [] ?APIKEY: string, + [] ?ProxyUsername: string, + [] ?ProxyPassword: string + ) = + Credentials() + |> Credentials.style ( + ?Username = Username, + ?APIKEY = APIKEY, + ?ProxyUsername = ProxyUsername, + ?ProxyPassword = ProxyPassword + ) + + static member style + ( + [] ?Username: string, + [] ?APIKEY: string, + [] ?ProxyUsername: string, + [] ?ProxyPassword: string + ) = + fun (credentials: Credentials) -> + + Username + |> DynObj.setValueOpt credentials "username" + + APIKEY |> DynObj.setValueOpt credentials "api_key" + + ProxyUsername + |> DynObj.setValueOpt credentials "proxy_username" + + ProxyPassword + |> DynObj.setValueOpt credentials "proxy_password" + + credentials + + static member getCredentialsFile() = + try + let json = + JObject.Parse(File.ReadAllText(CREDENTIALS_FILE)) + + Credentials.init ( + string json.["username"], + string json.["api_key"], + string json.["proxy_username"], + string json.["proxy_password"] + ) + with + | _ -> Credentials.init () + + static member setCredentialsFile + ( + [] ?Username: string, + [] ?APIKEY: string, + [] ?ProxyUsername: string, + [] ?ProxyPassword: string + ) = + let text = + Credentials() + |> Credentials.style ( + ?Username = Username, + ?APIKEY = APIKEY, + ?ProxyUsername = ProxyUsername, + ?ProxyPassword = ProxyPassword + ) + |> JsonConvert.SerializeObject + + try + File.WriteAllText(CREDENTIALS_FILE, text) + with + | e -> Trace.WriteLine(e.Message) + + + +type Config() = + inherit DynamicObj() + + static let PLOTLY_DIR = + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + + static let CONFIG_FILE = Path.Combine(PLOTLY_DIR, ".config") + + static member init + ( + [] ?Domain: string, + [] ?StreamingDomain: string, + [] ?APIDomain: string, + [] ?SSLVerification: bool, + [] ?ProxyAuthorization: bool, + [] ?WorldReadable: bool, + [] ?Sharing: string, + [] ?AutoOpen: bool + ) = + Config() + |> Config.style ( + ?Domain = Domain, + ?StreamingDomain = StreamingDomain, + ?APIDomain = APIDomain, + ?SSLVerification = SSLVerification, + ?ProxyAuthorization = ProxyAuthorization, + ?WorldReadable = WorldReadable, + ?Sharing = Sharing, + ?AutoOpen = AutoOpen + ) + + static member style + ( + [] ?Domain: string, + [] ?StreamingDomain: string, + [] ?APIDomain: string, + [] ?SSLVerification: bool, + [] ?ProxyAuthorization: bool, + [] ?WorldReadable: bool, + [] ?Sharing: string, + [] ?AutoOpen: bool + ) = + fun (config: Config) -> + + Domain + |> DynObj.setValueOpt config "plotly_domain" + + StreamingDomain + |> DynObj.setValueOpt config "plotly_streaming_domain" + + APIDomain + |> DynObj.setValueOpt config "plotly_api_domain" + + SSLVerification + |> DynObj.setValueOpt config "plotly_ssl_verification" + + ProxyAuthorization + |> DynObj.setValueOpt config "plotly_proxy_authorization" + + WorldReadable + |> DynObj.setValueOpt config "world_readable" + + Sharing |> DynObj.setValueOpt config "sharing" + + AutoOpen |> DynObj.setValueOpt config "auto_open" + + config + + static member getConfigFile() = + let getBool (jtoken: JToken) = + let _, result = bool.TryParse(string jtoken) + result + + try + let json = + JObject.Parse(File.ReadAllText(CONFIG_FILE)) + + Config.init ( + string json.["plotly_domain"], + string json.["plotly_streaming_domain"], + string json.["plotly_api_domain"], + getBool <| json.["plotly_ssl_verification"], + getBool <| json.["plotly_proxy_authorization"], + getBool <| json.["world_readable"], + string json.["sharing"], + getBool <| json.["auto_open"] + ) + with + | _ -> Config.init () + + static member setConfigFile + ( + [] ?Domain: string, + [] ?StreamingDomain: string, + [] ?APIDomain: string, + [] ?SSLVerification: bool, + [] ?ProxyAuthorization: bool, + [] ?WorldReadable: bool, + [] ?Sharing: string, + [] ?AutoOpen: bool + ) = + let text = + Config() + |> Config.style ( + ?Domain = Domain, + ?StreamingDomain = StreamingDomain, + ?APIDomain = APIDomain, + ?SSLVerification = SSLVerification, + ?ProxyAuthorization = ProxyAuthorization, + ?WorldReadable = WorldReadable, + ?Sharing = Sharing, + ?AutoOpen = AutoOpen + ) + |> JsonConvert.SerializeObject + + try + File.WriteAllText(CONFIG_FILE, text) + with + | e -> Trace.WriteLine(e.Message) diff --git a/src/Plotly.NET.ChartStudio/Plotly.NET.ChartStudio.fsproj b/src/Plotly.NET.ChartStudio/Plotly.NET.ChartStudio.fsproj new file mode 100644 index 000000000..22c657911 --- /dev/null +++ b/src/Plotly.NET.ChartStudio/Plotly.NET.ChartStudio.fsproj @@ -0,0 +1,23 @@ + + + + Exe + net5.0; netstandard2.0 + + + + + + + + + + + + + + + + + + diff --git a/src/Plotly.NET.ChartStudio/Program.fs b/src/Plotly.NET.ChartStudio/Program.fs new file mode 100644 index 000000000..5478a733d --- /dev/null +++ b/src/Plotly.NET.ChartStudio/Program.fs @@ -0,0 +1,17 @@ +// Learn more about F# at http://fsharp.org + +open System +open Plotly.NET +open Plotly.NET.ChartStudio + +[] +let main argv = + + ChartStudio.Credentials.setCredentialsFile (Username = "XXXX", APIKEY = "XXXXX") + + let chart = + Chart.Scatter(x = [ 0; 2; 3 ], y = [ 1; 2; 3 ], mode = StyleParam.Mode.Lines_Markers) + |> Chart.postToCloud ("base-line", true) + + printf "%A" chart + 0 // return an integer exit code