Skip to content

Add Kernel Extension for plotly formatter #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 9, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -186,4 +186,5 @@ docsrc/tools/FSharp.Formatting.svclog
docs
temp/gh-pages
/src/FSharp.Plotly/TestScript.fsx
/pkg
/pkg
.ionide/
8 changes: 8 additions & 0 deletions Plotly.NET.sln
Original file line number Diff line number Diff line change
@@ -83,6 +83,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".ci", ".ci", "{2461AFBF-6E1
.circleci\config.yml = .circleci\config.yml
EndProjectSection
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Plotly.NET.Interactive", "src\Plotly.NET.Interactive\Plotly.NET.Interactive.fsproj", "{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -102,6 +104,12 @@ Global
{2C9916F4-817A-4B70-8D83-F48E9A30544F}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
{2C9916F4-817A-4B70-8D83-F48E9A30544F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C9916F4-817A-4B70-8D83-F48E9A30544F}.Release|Any CPU.Build.0 = Release|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Dotnet|Any CPU.ActiveCfg = Debug|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Dotnet|Any CPU.Build.0 = Debug|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F135E3B-B0E1-42A9-B180-18C0221DC7B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
19 changes: 17 additions & 2 deletions paket.dependencies
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ source https://nuget.org/api/v2

nuget FSharp.Core >= 4.7
nuget Newtonsoft.Json >= 12.0.3
nuget Argu
nuget Argu

nuget System.Runtime.InteropServices.RuntimeInformation >= 4.3.0

@@ -14,4 +14,19 @@ source https://nuget.org/api/v2
source https://ci.appveyor.com/nuget/fsharp-formatting

nuget FSharp.Formatting prerelease
nuget FSharp.Formatting.CommandTool prerelease
nuget FSharp.Formatting.CommandTool prerelease

group DotnetInteractive
# until .NET Interactive is out of beta most packages needed to write extension
# dont exist in the public repositories - consumers of the nuget shouldn't need this
source https://api.nuget.org/v3/index.json
source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json
source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet3.1/nuget/v3/index.json
source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json
source https://pkgs.dev.azure.com/dnceng/public/_packaging/MachineLearning/nuget/v3/index.json
source https://www.powershellgallery.com/api/v2

nuget Microsoft.DotNet.Interactive 1.0.0-beta.20574.9
nuget Microsoft.DotNet.Interactive.Formatting 1.0.0-beta.20574.9
nuget Microsoft.DotNet.Interactive.FSharp 1.0.0-beta.20574.9
406 changes: 308 additions & 98 deletions paket.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/Plotly.NET.Interactive/AssemblyInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Auto-Generated by FAKE; do not edit
namespace System
open System.Reflection

[<assembly: AssemblyTitleAttribute("Plotly.NET.Interactive")>]
[<assembly: AssemblyProductAttribute("Plotly.NET")>]
[<assembly: AssemblyDescriptionAttribute("A F# interactive charting library using plotly.js")>]
[<assembly: AssemblyVersionAttribute("2.0.0")>]
[<assembly: AssemblyFileVersionAttribute("2.0.0")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "Plotly.NET.Interactive"
let [<Literal>] AssemblyProduct = "Plotly.NET"
let [<Literal>] AssemblyDescription = "A F# interactive charting library using plotly.js"
let [<Literal>] AssemblyVersion = "2.0.0"
let [<Literal>] AssemblyFileVersion = "2.0.0"
29 changes: 29 additions & 0 deletions src/Plotly.NET.Interactive/Extension.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Plotly.NET.Interactive

open System.Threading.Tasks
open Microsoft.DotNet.Interactive
open Microsoft.DotNet.Interactive.Formatting
open Plotly.NET.GenericChart

type FormatterKernelExtension() =

let registerFormatter () =
Formatter.Register<GenericChart>
((fun chart writer ->
let html = toChartHTML chart

writer.Write(html)),
HtmlFormatter.MimeType)

interface IKernelExtension with
member _.OnLoadAsync _ =
registerFormatter ()

if isNull KernelInvocationContext.Current |> not then
let message =
"Added Kernerl Extension including formatters for GenericChart"

KernelInvocationContext.Current.Display(message, "text/markdown")
|> ignore

Task.CompletedTask
1,219 changes: 1,219 additions & 0 deletions src/Plotly.NET.Interactive/ExtensionVisualTest.ipynb

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions src/Plotly.NET.Interactive/Plotly.NET.Interactive.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>preview</LangVersion>
<OutputType>Library</OutputType>
<NoWarn>$(NoWarn);NU5100</NoWarn><!-- dll outside of lib/ folder -->
</PropertyGroup>

<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Extension.fs" />
</ItemGroup>

<ItemGroup>
<Compile Remove="bin\**" />
<EmbeddedResource Remove="bin\**" />
<None Remove="bin\**" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Plotly.NET\Plotly.NET.fsproj" />
</ItemGroup>

<Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>
6 changes: 6 additions & 0 deletions src/Plotly.NET.Interactive/paket.references
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
group DotnetInteractive

FSharp.Core
Microsoft.DotNet.Interactive
Microsoft.DotNet.Interactive.Formatting
Microsoft.DotNet.Interactive.FSharp
33 changes: 33 additions & 0 deletions src/Plotly.NET.Interactive/paket.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
type project
id Plotly.NET.Interactive
title
Plotly.NET.Interactive
owners
Timo Mühlhaus, Kevin Schneider
authors
Timo Mühlhaus, Kevin Schneider, Gregor Beyerle, F# open source contributors
projectUrl
https://plotly.github.io/Plotly.NET/
iconUrl
https://raw.githubusercontent.com/plotly/Plotly.NET/master/docsrc/files/img/logo.png
licenseUrl
https://github.com/plotly/Plotly.NET/blob/master/LICENSE
requireLicenseAcceptance
false
language
F#
copyright
Copyright 2017-2020
tags
visualization charting plotly F#
summary
A .NET Interactive Kernel Extension for displaying Plotly.NET charts
description
A .NET Interactive Kernel Extension for displaying Plotly.NET charts
files
bin/Release/netstandard2.1/Plotly.NET.Interactive.dll ==> interactive-extensions/dotnet
repositoryType
git
repositoryUrl
https://github.com/plotly/Plotly.NET
include-pdbs false
82 changes: 41 additions & 41 deletions src/Plotly.NET/GenericChart.fs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ open System
open Newtonsoft.Json
open System.Runtime.CompilerServices

/// HTML template for Plotly.js
/// HTML template for Plotly.js
module HTML =

let doc =
@@ -60,7 +60,7 @@ module HTML =
newScript.AppendLine("<script type=\"text/javascript\">") |> ignore
newScript.AppendLine(@"
var renderPlotly = function() {
var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'https://cdn.plot.ly/plotly-latest.min'}});
var fsharpPlotlyRequire = requirejs.config({context:'fsharp-plotly',paths:{plotly:'https://cdn.plot.ly/plotly-latest.min'}}) || require;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also saw this one in your blog post, could you give me some info why we need to do this? My js is 'basic' 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess would be, that hoisting works a bit different in Electron from how it works in most browsers. When calling the immediately executed anonymous function to set up the plotly chart it tries to use the fsharpPlotlyRequire function and dies because it is undefined at the time. In other browser environments this doesn't happen. Why does this difference exist: beats me. You can try out how it behaves in VSCode (with the .NET interactive plugin) when you don't have this expression. It should work in your normal Jupyter environment, though. I can't talk about Nteract because I didn't test it there. Should also just happen for the recent versions of VSCode Insiders because they changed something there (my guess would be, that they switched to a newer Electron version, but I'm not sure).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well that's strange. I guess as long as this change does not affect the other environments we can take it.

fsharpPlotlyRequire(['plotly'], function(Plotly) {") |> ignore
newScript.AppendLine(@"
var data = [DATA];
@@ -69,13 +69,13 @@ module HTML =
Plotly.newPlot('[ID]', data, layout, config);") |> ignore
newScript.AppendLine("""});
};
if ((typeof(requirejs) !== typeof(Function)) || (typeof(requirejs.config) !== typeof(Function))) {
var script = document.createElement("script");
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js");
if ((typeof(requirejs) !== typeof(Function)) || (typeof(requirejs.config) !== typeof(Function))) {
var script = document.createElement("script");
script.setAttribute("src", "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js");
script.onload = function(){
renderPlotly();
};
document.getElementsByTagName("head")[0].appendChild(script);
document.getElementsByTagName("head")[0].appendChild(script);
}
else {
renderPlotly();
@@ -112,14 +112,14 @@ module HTML =
function(url)
{
img_jpg.attr("src", url);
}
)
});
</script>"""

module ChartDescription =

type Description =
{
Heading : string
@@ -138,15 +138,15 @@ module GenericChart =
open Trace
open ChartDescription

type Figure =
type Figure =
{
[<JsonProperty("data")>]
Data: Trace list
[<JsonProperty("layout")>]
Layout: Layout
[<JsonProperty("frames")>]
Frames: Frame list
}
}
static member create data layout = {Data = data; Layout = layout; Frames=[]}

//TO-DO refactor as type with static members to remove verbose top namespace from 'GenericChart.GenericChart'
@@ -194,7 +194,7 @@ module GenericChart =
/// Returns a tuple containing the width and height of a GenericChart's layout if the property is set, otherwise returns None
let tryGetLayoutSize gChart =
let layout = getLayout gChart
let width,height =
let width,height =
layout.TryGetTypedValue<float> "width",
layout.TryGetTypedValue<float> "height"
match (width,height) with
@@ -217,35 +217,35 @@ module GenericChart =
// | Chart (trace,_) ->
// let l' = getLayouts gChart
// Chart (trace,Some (layouts@l'))
// | MultiChart (traces,_) ->
// | MultiChart (traces,_) ->
// let l' = getLayouts gChart
// MultiChart (traces, Some (layouts@l'))

// Combines two GenericChart
let combine(gCharts:seq<GenericChart>) =
let combineLayouts (first:Layout) (second:Layout) =
let combineLayouts (first:Layout) (second:Layout) =
DynObj.combine first second |> unbox

let combineConfigs (first:Config) (second:Config) =
let combineConfigs (first:Config) (second:Config) =
DynObj.combine first second |> unbox

gCharts
|> Seq.reduce (fun acc elem ->
match acc,elem with
| MultiChart (traces,l1,c1),Chart (trace,l2,c2) ->
| MultiChart (traces,l1,c1),Chart (trace,l2,c2) ->
MultiChart (List.append traces [trace], combineLayouts l1 l2, combineConfigs c1 c2)
| MultiChart (traces1,l1,c1),MultiChart (traces2,l2,c2) ->
| MultiChart (traces1,l1,c1),MultiChart (traces2,l2,c2) ->
MultiChart (List.append traces1 traces2,combineLayouts l1 l2, combineConfigs c1 c2)
| Chart (trace1,l1,c1),Chart (trace2,l2,c2) ->
| Chart (trace1,l1,c1),Chart (trace2,l2,c2) ->
MultiChart ([trace1;trace2],combineLayouts l1 l2, combineConfigs c1 c2)
| Chart (trace,l1,c1),MultiChart (traces,l2,c2) ->
| Chart (trace,l1,c1),MultiChart (traces,l2,c2) ->
MultiChart (List.append [trace] traces ,combineLayouts l1 l2, combineConfigs c1 c2)
)
)

// let private materialzeLayout (layout:(Layout -> Layout) list) =
// let rec reduce fl v =
// match fl with
// | h::t -> reduce t (h v)
// | h::t -> reduce t (h v)
// | [] -> v

// // Attention order ov layout functions is reverse
@@ -258,13 +258,13 @@ module GenericChart =
let guid = Guid.NewGuid().ToString()
let tracesJson =
getTraces gChart
|> JsonConvert.SerializeObject
let layoutJson =
|> JsonConvert.SerializeObject
let layoutJson =
getLayout gChart
|> JsonConvert.SerializeObject
|> JsonConvert.SerializeObject
let configJson =
getConfig gChart
|> JsonConvert.SerializeObject
|> JsonConvert.SerializeObject

let dims = tryGetLayoutSize gChart
let width,height =
@@ -283,18 +283,18 @@ module GenericChart =
.Replace("[CONFIG]", configJson)
html

/// Converts a GenericChart to it HTML representation and set the size of the div
/// Converts a GenericChart to it HTML representation and set the size of the div
let toChartHtmlWithSize (width:int) (height:int) (gChart:GenericChart) =
let guid = Guid.NewGuid().ToString()
let tracesJson =
getTraces gChart
|> JsonConvert.SerializeObject
let layoutJson =
getLayout gChart
|> JsonConvert.SerializeObject
let configJson =
|> JsonConvert.SerializeObject
let layoutJson =
getLayout gChart
|> JsonConvert.SerializeObject
let configJson =
getConfig gChart
|> JsonConvert.SerializeObject
|> JsonConvert.SerializeObject

let html =
HTML.chart
@@ -320,7 +320,7 @@ module GenericChart =


/// Converts a GenericChart to it HTML representation and embeds it into a html page.
let toEmbeddedHTML gChart =
let toEmbeddedHTML gChart =
let chartMarkup =
toChartHTML gChart

@@ -333,10 +333,10 @@ module GenericChart =
let guid = Guid.NewGuid().ToString()
let tracesJson =
getTraces gChart
|> JsonConvert.SerializeObject
let layoutJson =
|> JsonConvert.SerializeObject
let layoutJson =
getLayout gChart
|> JsonConvert.SerializeObject
|> JsonConvert.SerializeObject

let html =
HTML.staticChart
@@ -361,18 +361,18 @@ module GenericChart =
html


/// Creates a new GenericChart whose traces are the results of applying the given function to each of the trace of the GenericChart.
/// Creates a new GenericChart whose traces are the results of applying the given function to each of the trace of the GenericChart.
let mapTrace f gChart =
match gChart with
| Chart (trace,layout,config) -> Chart (f trace,layout,config)
| MultiChart (traces,layout,config) -> MultiChart (traces |> List.map f,layout,config)
| MultiChart (traces,layout,config) -> MultiChart (traces |> List.map f,layout,config)

/// Creates a new GenericChart whose traces are the results of applying the given function to each of the trace of the GenericChart.
/// The integer index passed to the function indicates the index (from 0) of element being transformed.
let mapiTrace f gChart =
match gChart with
| Chart (trace,layout,config) -> Chart (f 0 trace,layout,config)
| MultiChart (traces,layout,config) -> MultiChart (traces |> List.mapi f,layout,config)
| MultiChart (traces,layout,config) -> MultiChart (traces |> List.mapi f,layout,config)

/// Returns the number of traces within the GenericChart
let countTrace gChart =
@@ -383,9 +383,9 @@ module GenericChart =
/// Creates a new GenericChart whose traces are the results of applying the given function to each of the trace of the GenericChart.
let existsTrace (f:Trace->bool) gChart =
match gChart with
| Chart (trace,_,_) -> f trace
| Chart (trace,_,_) -> f trace
| MultiChart (traces,_,_) -> traces |> List.exists f

/// Converts from a trace object and a layout object into GenericChart
let ofTraceObject trace = //layout =
GenericChart.Chart(trace, Layout(), Config())