Skip to content

Static notebook fixes and examples #511

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 7 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,17 @@ public fun <T> DataFrame<T>.html(): String = toStandaloneHTML().toString()
public fun <T> DataFrame<T>.toStandaloneHTML(
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
cellRenderer: CellRenderer = DefaultCellRenderer,
includeStatic: Boolean = true,
getFooter: (DataFrame<T>) -> String? = { "DataFrame [${it.size}]" },
): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions()
): DataFrameHtmlData = toHTML(configuration, cellRenderer, includeStatic, getFooter).withTableDefinitions()

/**
* @return DataFrameHtmlData without additional definitions. Can be rendered in Jupyter kernel environments
*/
public fun <T> DataFrame<T>.toHTML(
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
cellRenderer: CellRenderer = DefaultCellRenderer,
includeStatic: Boolean = true,
getFooter: (DataFrame<T>) -> String? = { "DataFrame [${it.size}]" },
): DataFrameHtmlData {
val limit = configuration.rowsLimit ?: Int.MAX_VALUE
Expand All @@ -283,7 +285,9 @@ public fun <T> DataFrame<T>.toHTML(

var tableHtml = toHtmlData(configuration, cellRenderer)

tableHtml += toStaticHtml(configuration, DefaultCellRenderer)
if (includeStatic) {
tableHtml += toStaticHtml(configuration, DefaultCellRenderer)
}

if (bodyFooter != null) {
tableHtml += DataFrameHtmlData("", bodyFooter, "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
import org.jetbrains.kotlinx.dataframe.io.encodeFrame
import org.jetbrains.kotlinx.dataframe.io.toHTML
import org.jetbrains.kotlinx.dataframe.io.toStaticHtml
import org.jetbrains.kotlinx.dataframe.jupyter.KotlinNotebookPluginUtils.convertToDataFrame
import org.jetbrains.kotlinx.dataframe.nrow
import org.jetbrains.kotlinx.dataframe.size
Expand All @@ -30,7 +31,7 @@ internal class JupyterHtmlRenderer(
internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
noinline getFooter: (T) -> String,
crossinline modifyConfig: T.(DisplayConfiguration) -> DisplayConfiguration = { it },
applyRowsLimit: Boolean = true
applyRowsLimit: Boolean = true,
) = builder.renderWithHost<T> { host, value ->
val contextRenderer = JupyterCellRenderer(this.notebook, host)
val reifiedDisplayConfiguration = value.modifyConfig(display)
Expand All @@ -44,36 +45,41 @@ internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
df.nrow
}

val html = (
DataFrameHtmlData.tableDefinitions(
includeJs = reifiedDisplayConfiguration.isolatedOutputs,
includeCss = true
) + df.toHTML(
reifiedDisplayConfiguration,
contextRenderer
val html = DataFrameHtmlData.tableDefinitions(
includeJs = reifiedDisplayConfiguration.isolatedOutputs,
includeCss = true,
).plus(
df.toHTML(
configuration = reifiedDisplayConfiguration,
cellRenderer = contextRenderer,
includeStatic = false, // is added later to make sure it's put outside of potential iFrames
) { footer }
).toJupyterHtmlData()
).toJupyterHtmlData()

// Generates a static version of the table which can be displayed in GitHub previews etc.
val staticHtml = df.toStaticHtml(reifiedDisplayConfiguration, DefaultCellRenderer).toJupyterHtmlData()

if (notebook.kernelVersion >= KotlinKernelVersion.from(MIN_KERNEL_VERSION_FOR_NEW_TABLES_UI)!!) {
val jsonEncodedDf = json {
obj(
"nrow" to df.size.nrow,
"ncol" to df.size.ncol,
"columns" to df.columnNames(),
"kotlin_dataframe" to encodeFrame(df.rows().take(limit).toDataFrame())
"kotlin_dataframe" to encodeFrame(df.rows().take(limit).toDataFrame()),
)
}.toJsonString()
notebook.renderAsIFrameAsNeeded(html, jsonEncodedDf)
notebook.renderAsIFrameAsNeeded(html, staticHtml, jsonEncodedDf)
} else {
notebook.renderHtmlAsIFrameIfNeeded(html)
}
}

internal fun Notebook.renderAsIFrameAsNeeded(data: HtmlData, jsonEncodedDf: String): MimeTypedResult {
internal fun Notebook.renderAsIFrameAsNeeded(data: HtmlData, staticData: HtmlData, jsonEncodedDf: String): MimeTypedResult {
val textHtml = if (jupyterClientType == JupyterClientType.KOTLIN_NOTEBOOK) {
data.generateIframePlaneText(currentColorScheme)
data.generateIframePlaneText(currentColorScheme) +
staticData.toString(currentColorScheme)
} else {
data.toString(currentColorScheme)
(data + staticData).toString(currentColorScheme)
}

return mimeResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,17 @@ public fun <T> DataFrame<T>.html(): String = toStandaloneHTML().toString()
public fun <T> DataFrame<T>.toStandaloneHTML(
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
cellRenderer: CellRenderer = DefaultCellRenderer,
includeStatic: Boolean = true,
getFooter: (DataFrame<T>) -> String? = { "DataFrame [${it.size}]" },
): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions()
): DataFrameHtmlData = toHTML(configuration, cellRenderer, includeStatic, getFooter).withTableDefinitions()

/**
* @return DataFrameHtmlData without additional definitions. Can be rendered in Jupyter kernel environments
*/
public fun <T> DataFrame<T>.toHTML(
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
cellRenderer: CellRenderer = DefaultCellRenderer,
includeStatic: Boolean = true,
getFooter: (DataFrame<T>) -> String? = { "DataFrame [${it.size}]" },
): DataFrameHtmlData {
val limit = configuration.rowsLimit ?: Int.MAX_VALUE
Expand All @@ -283,7 +285,9 @@ public fun <T> DataFrame<T>.toHTML(

var tableHtml = toHtmlData(configuration, cellRenderer)

tableHtml += toStaticHtml(configuration, DefaultCellRenderer)
if (includeStatic) {
tableHtml += toStaticHtml(configuration, DefaultCellRenderer)
}

if (bodyFooter != null) {
tableHtml += DataFrameHtmlData("", bodyFooter, "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
import org.jetbrains.kotlinx.dataframe.io.encodeFrame
import org.jetbrains.kotlinx.dataframe.io.toHTML
import org.jetbrains.kotlinx.dataframe.io.toStaticHtml
import org.jetbrains.kotlinx.dataframe.jupyter.KotlinNotebookPluginUtils.convertToDataFrame
import org.jetbrains.kotlinx.dataframe.nrow
import org.jetbrains.kotlinx.dataframe.size
Expand All @@ -30,7 +31,7 @@ internal class JupyterHtmlRenderer(
internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
noinline getFooter: (T) -> String,
crossinline modifyConfig: T.(DisplayConfiguration) -> DisplayConfiguration = { it },
applyRowsLimit: Boolean = true
applyRowsLimit: Boolean = true,
) = builder.renderWithHost<T> { host, value ->
val contextRenderer = JupyterCellRenderer(this.notebook, host)
val reifiedDisplayConfiguration = value.modifyConfig(display)
Expand All @@ -44,36 +45,41 @@ internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
df.nrow
}

val html = (
DataFrameHtmlData.tableDefinitions(
includeJs = reifiedDisplayConfiguration.isolatedOutputs,
includeCss = true
) + df.toHTML(
reifiedDisplayConfiguration,
contextRenderer
val html = DataFrameHtmlData.tableDefinitions(
includeJs = reifiedDisplayConfiguration.isolatedOutputs,
includeCss = true,
).plus(
df.toHTML(
configuration = reifiedDisplayConfiguration,
cellRenderer = contextRenderer,
includeStatic = false, // is added later to make sure it's put outside of potential iFrames
) { footer }
).toJupyterHtmlData()
).toJupyterHtmlData()

// Generates a static version of the table which can be displayed in GitHub previews etc.
val staticHtml = df.toStaticHtml(reifiedDisplayConfiguration, DefaultCellRenderer).toJupyterHtmlData()

if (notebook.kernelVersion >= KotlinKernelVersion.from(MIN_KERNEL_VERSION_FOR_NEW_TABLES_UI)!!) {
val jsonEncodedDf = json {
obj(
"nrow" to df.size.nrow,
"ncol" to df.size.ncol,
"columns" to df.columnNames(),
"kotlin_dataframe" to encodeFrame(df.rows().take(limit).toDataFrame())
"kotlin_dataframe" to encodeFrame(df.rows().take(limit).toDataFrame()),
)
}.toJsonString()
notebook.renderAsIFrameAsNeeded(html, jsonEncodedDf)
notebook.renderAsIFrameAsNeeded(html, staticHtml, jsonEncodedDf)
} else {
notebook.renderHtmlAsIFrameIfNeeded(html)
}
}

internal fun Notebook.renderAsIFrameAsNeeded(data: HtmlData, jsonEncodedDf: String): MimeTypedResult {
internal fun Notebook.renderAsIFrameAsNeeded(data: HtmlData, staticData: HtmlData, jsonEncodedDf: String): MimeTypedResult {
val textHtml = if (jupyterClientType == JupyterClientType.KOTLIN_NOTEBOOK) {
data.generateIframePlaneText(currentColorScheme)
data.generateIframePlaneText(currentColorScheme) +
staticData.toString(currentColorScheme)
} else {
data.toString(currentColorScheme)
(data + staticData).toString(currentColorScheme)
}

return mimeResult(
Expand Down
Loading