Created
July 5, 2020 19:29
-
-
Save kMutagene/77aca4941e89f96bdbf5700f45ff27b3 to your computer and use it in GitHub Desktop.
Kaleido.FSharp POC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Use F#5 preview to use "#r:nuget..." | |
#r "nuget:FSharp.Plotly,Version=2.0.0-alpha" | |
#r "nuget:Fake.Core.Process" | |
open System | |
open System.Diagnostics | |
open Fake.IO | |
open FSharp.Plotly | |
open Newtonsoft.Json | |
let randomPoints = | |
let rnd = new System.Random() | |
[for i = 0 to 999 do yield rnd.NextDouble(),rnd.NextDouble()] | |
let testFig = | |
Chart.Point( | |
randomPoints | |
) | |
|> Chart.withX_AxisStyle("TestTitle_X",Showgrid=false) | |
|> Chart.withY_AxisStyle("TestTitle_Y",Showgrid=false) | |
testFig |> Chart.Show | |
module Kaleido = | |
//input and output should most likely be dynamic objects themselves instead of record types | |
type KaleidoInput () = | |
inherit DynamicObj() | |
///This function makes it possible to render Charts | |
///generated with FSharp.Plotly | |
static member ofChart | |
( | |
format:string, | |
gChart:GenericChart.GenericChart | |
) = | |
let data = GenericChart.getTraces gChart | |
let layout = GenericChart.getLayout gChart | |
let kI = KaleidoInput() | |
data |> DynObj.setValue kI "data" | |
layout |> DynObj.setValue kI "layout" | |
format |> DynObj.setValue kI "format" | |
kI | |
//TODO: Extend with all fields possibly returned by Kaleido and handle null properly | |
type KaleidoResponse = { | |
Code : int | |
Message : string | |
Format : string | |
Result : string | |
} | |
with | |
static member fromJsonString (jsonString) = | |
jsonString |> JsonConvert.DeserializeObject<KaleidoResponse> | |
///Starts a process using the kaleido executable and a scope name | |
//TODO: abstract scopes as types | |
let start executablePath scopeName = | |
let startInfo = | |
new ProcessStartInfo( | |
UseShellExecute = false, | |
RedirectStandardOutput = true, | |
RedirectStandardInput = true, | |
RedirectStandardError = false, | |
FileName = executablePath, | |
Arguments = scopeName | |
) | |
let kaleidoProcess = new Process(StartInfo = startInfo) | |
if kaleidoProcess.Start() then | |
let startupMsg = kaleidoProcess.StandardOutput.ReadLine() | |
printfn "%s" startupMsg | |
kaleidoProcess | |
else | |
failwith "Error starting Kaleido process" | |
let stop (kaleidoProcess:System.Diagnostics.Process) = | |
kaleidoProcess.Kill() | |
kaleidoProcess.Dispose() | |
///Render the given KaleidoInput using the given process. | |
///use keepRunning = true if you want to use the process again afterwards. | |
let render (kaleidoProcess:System.Diagnostics.Process) (keepRunning:bool) (input:KaleidoInput) = | |
input | |
|> JsonConvert.SerializeObject | |
|> kaleidoProcess.StandardInput.WriteLine | |
let result = | |
kaleidoProcess.StandardOutput.ReadLine() | |
|> KaleidoResponse.fromJsonString | |
if not keepRunning then stop kaleidoProcess | |
result | |
//Example usage: | |
//Path to kaleido executable. | |
let kaleidoPath = @"path/to/kaleido.cmd" | |
//Start a kaleido process | |
let proc = Kaleido.start kaleidoPath "plotly" | |
//Formats dont seem to work as I expected. Changing format here does nothing. | |
let input = Kaleido.KaleidoInput.ofChart("png",testFig) | |
//Render the chart with the started process. Save image using FAKE | |
input | |
|> Kaleido.render proc true | |
|> fun r -> | |
//save result as image | |
r.Result | |
|> Convert.FromBase64String | |
|> File.writeBytes ( | |
sprintf | |
@"path/to/kaleido.cmd\testFigure.%s" | |
r.Format | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I played with F# several years ago for a class project, and I love the language. But I never got deep enough into to it to understand how packaging works. And that was before .NET core, so I'm also not clear on the Linux packaging story right now.
Cool! If you could include bash/cmd scripts that build the packages, given a path to the executable directory, I think that would be enough for me to use to integrate it into CI. What I'd be doing would be to create a CI task that runs after the executable is created, builds the package, and then uploads it to circleci as an artifact. Then I'd look to you for help on how to publish these packages.
I haven't added the Python tests to CI yet, but that's something I'll work on soon.