Skip to content

Commit b925181

Browse files
committed
update generated sources
1 parent 7bcaeda commit b925181

File tree

7 files changed

+177
-49
lines changed

7 files changed

+177
-49
lines changed

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/api/format.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import org.jetbrains.kotlinx.dataframe.impl.api.formatImpl
1414
import org.jetbrains.kotlinx.dataframe.impl.api.linearGradient
1515
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
1616
import org.jetbrains.kotlinx.dataframe.io.toHTML
17-
import org.jetbrains.kotlinx.jupyter.api.HtmlData
17+
import org.jetbrains.kotlinx.dataframe.io.toStandaloneHTML
1818
import kotlin.reflect.KProperty
19+
import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
1920

2021
// region DataFrame
2122

@@ -98,7 +99,19 @@ public class FormattedFrame<T>(
9899
internal val df: DataFrame<T>,
99100
internal val formatter: RowColFormatter<T, *>? = null,
100101
) {
101-
public fun toHTML(configuration: DisplayConfiguration): HtmlData = df.toHTML(getDisplayConfiguration(configuration))
102+
/**
103+
* @return DataFrameHtmlData without additional definitions. Can be rendered in Jupyter kernel environments
104+
*/
105+
public fun toHTML(configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT): DataFrameHtmlData {
106+
return df.toHTML(getDisplayConfiguration(configuration))
107+
}
108+
109+
/**
110+
* @return DataFrameHtmlData with table script and css definitions. Can be saved as an *.html file and displayed in the browser
111+
*/
112+
public fun toStandaloneHTML(configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT): DataFrameHtmlData {
113+
return df.toStandaloneHTML(getDisplayConfiguration(configuration))
114+
}
102115

103116
public fun getDisplayConfiguration(configuration: DisplayConfiguration): DisplayConfiguration {
104117
return configuration.copy(cellFormatter = formatter as RowColFormatter<*, *>?)

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.jetbrains.kotlinx.dataframe.io
22

3+
import org.intellij.lang.annotations.Language
34
import org.jetbrains.kotlinx.dataframe.AnyCol
45
import org.jetbrains.kotlinx.dataframe.AnyFrame
56
import org.jetbrains.kotlinx.dataframe.AnyRow
@@ -21,11 +22,14 @@ import org.jetbrains.kotlinx.dataframe.jupyter.RenderedContent
2122
import org.jetbrains.kotlinx.dataframe.name
2223
import org.jetbrains.kotlinx.dataframe.nrow
2324
import org.jetbrains.kotlinx.dataframe.size
24-
import org.jetbrains.kotlinx.jupyter.api.HtmlData
25+
import java.awt.Desktop
26+
import java.io.File
2527
import java.io.InputStreamReader
2628
import java.net.URL
29+
import java.nio.file.Path
2730
import java.util.LinkedList
2831
import java.util.Random
32+
import kotlin.io.path.writeText
2933

3034
internal val tooltipLimit = 1000
3135

@@ -116,7 +120,7 @@ internal fun nextTableId() = sessionId + (tableInSessionId++)
116120
internal fun AnyFrame.toHtmlData(
117121
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
118122
cellRenderer: CellRenderer,
119-
): HtmlData {
123+
): DataFrameHtmlData {
120124
val scripts = mutableListOf<String>()
121125
val queue = LinkedList<Pair<AnyFrame, Int>>()
122126

@@ -165,30 +169,31 @@ internal fun AnyFrame.toHtmlData(
165169
}
166170
val body = getResourceText("/table.html", "ID" to rootId)
167171
val script = scripts.joinToString("\n") + "\n" + getResourceText("/renderTable.js", "___ID___" to rootId)
168-
return HtmlData("", body, script)
172+
return DataFrameHtmlData("", body, script)
169173
}
170174

171-
internal fun HtmlData.print() = println(this)
172-
173-
internal fun initHtml(
174-
includeJs: Boolean = true,
175-
includeCss: Boolean = true,
176-
useDarkColorScheme: Boolean = false,
177-
): HtmlData =
178-
HtmlData(
179-
style = if (includeCss) getResources("/table.css") else "",
180-
script = if (includeJs) getResourceText("/init.js") else "",
181-
body = "",
182-
)
175+
internal fun DataFrameHtmlData.print() = println(this)
176+
177+
@Deprecated("Clarify difference with .toHTML()", ReplaceWith("this.toStandaloneHTML().toString()", "org.jetbrains.kotlinx.dataframe.io.toStandaloneHTML"))
178+
public fun <T> DataFrame<T>.html(): String = toStandaloneHTML().toString()
183179

184-
public fun <T> DataFrame<T>.html(): String = toHTML(extraHtml = initHtml()).toString()
180+
/**
181+
* @return DataFrameHtmlData with table script and css definitions. Can be saved as an *.html file and displayed in the browser
182+
*/
183+
public fun <T> DataFrame<T>.toStandaloneHTML(
184+
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
185+
cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer,
186+
getFooter: (DataFrame<T>) -> String = { "DataFrame [${it.size}]" },
187+
): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions()
185188

189+
/**
190+
* @return DataFrameHtmlData without additional definitions. Can be rendered in Jupyter kernel environments
191+
*/
186192
public fun <T> DataFrame<T>.toHTML(
187193
configuration: DisplayConfiguration = DisplayConfiguration.DEFAULT,
188-
extraHtml: HtmlData? = null,
189194
cellRenderer: CellRenderer = org.jetbrains.kotlinx.dataframe.jupyter.DefaultCellRenderer,
190195
getFooter: (DataFrame<T>) -> String = { "DataFrame [${it.size}]" },
191-
): HtmlData {
196+
): DataFrameHtmlData {
192197
val limit = configuration.rowsLimit ?: Int.MAX_VALUE
193198

194199
val footer = getFooter(this)
@@ -204,11 +209,79 @@ public fun <T> DataFrame<T>.toHTML(
204209
}
205210

206211
val tableHtml = toHtmlData(configuration, cellRenderer)
207-
val html = tableHtml + HtmlData("", bodyFooter, "")
208212

209-
return if (extraHtml != null) extraHtml + html else html
213+
return tableHtml + DataFrameHtmlData("", bodyFooter, "")
214+
}
215+
216+
/**
217+
* Container for HTML page data in form of String
218+
* Can be used to compose rendered dataframe tables with additional HTML elements
219+
*/
220+
public data class DataFrameHtmlData(val style: String = "", val body: String = "", val script: String = "") {
221+
@Language("html")
222+
override fun toString(): String = """
223+
<html>
224+
<head>
225+
<style type="text/css">
226+
$style
227+
</style>
228+
</head>
229+
<body>
230+
$body
231+
</body>
232+
<script>
233+
$script
234+
</script>
235+
</html>
236+
""".trimIndent()
237+
238+
public operator fun plus(other: DataFrameHtmlData): DataFrameHtmlData =
239+
DataFrameHtmlData(
240+
style + "\n" + other.style,
241+
body + "\n" + other.body,
242+
script + "\n" + other.script,
243+
)
244+
245+
public fun writeHTML(destination: File) {
246+
destination.writeText(toString())
247+
}
248+
249+
public fun writeHTML(destination: Path) {
250+
destination.writeText(toString())
251+
}
252+
253+
public fun openInBrowser() {
254+
val file = File.createTempFile("df_rendering", ".html")
255+
writeHTML(file)
256+
val uri = file.toURI()
257+
val desktop = Desktop.getDesktop()
258+
desktop.browse(uri)
259+
}
260+
261+
public fun withTableDefinitions(): DataFrameHtmlData = tableDefinitions() + this
262+
263+
public companion object {
264+
/**
265+
* @return CSS and JS required to render DataFrame tables
266+
* Can be used as a starting point to create page with multiple tables
267+
* @see DataFrame.toHTML
268+
* @see DataFrameHtmlData.plus
269+
*/
270+
public fun tableDefinitions(
271+
includeJs: Boolean = true,
272+
includeCss: Boolean = true,
273+
): DataFrameHtmlData = DataFrameHtmlData(
274+
style = if (includeCss) getResources("/table.css") else "",
275+
script = if (includeJs) getResourceText("/init.js") else "",
276+
body = "",
277+
)
278+
}
210279
}
211280

281+
/**
282+
* @param rowsLimit null to disable rows limit
283+
* @param cellContentLimit -1 to disable content trimming
284+
*/
212285
public data class DisplayConfiguration(
213286
var rowsLimit: Int? = 20,
214287
var nestedRowsLimit: Int? = 5,
@@ -452,7 +525,7 @@ internal class DataFrameFormatter(
452525
"</a>"
453526
)
454527

455-
is HtmlData -> RenderedContent.text(value.body)
528+
is DataFrameHtmlData -> RenderedContent.text(value.body)
456529
else -> renderer.content(value, configuration)
457530
}
458531
if (result != null && result.textLength > configuration.cellContentLimit) return null

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/Integration.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import org.jetbrains.kotlinx.dataframe.impl.codeGen.CodeGenerationReadResult
1717
import org.jetbrains.kotlinx.dataframe.impl.codeGen.urlCodeGenReader
1818
import org.jetbrains.kotlinx.dataframe.impl.createStarProjectedType
1919
import org.jetbrains.kotlinx.dataframe.impl.renderType
20+
import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
2021
import org.jetbrains.kotlinx.dataframe.io.SupportedCodeGenerationFormat
2122
import org.jetbrains.kotlinx.dataframe.io.supportedFormats
2223
import org.jetbrains.kotlinx.jupyter.api.HTML
23-
import org.jetbrains.kotlinx.jupyter.api.HtmlData
2424
import org.jetbrains.kotlinx.jupyter.api.JupyterClientType
2525
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost
2626
import org.jetbrains.kotlinx.jupyter.api.Notebook
@@ -29,7 +29,6 @@ import org.jetbrains.kotlinx.jupyter.api.declare
2929
import org.jetbrains.kotlinx.jupyter.api.libraries.ColorScheme
3030
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
3131
import org.jetbrains.kotlinx.jupyter.api.libraries.resources
32-
import org.jetbrains.kotlinx.jupyter.api.renderHtmlAsIFrameIfNeeded
3332
import kotlin.reflect.KClass
3433
import kotlin.reflect.KProperty
3534
import kotlin.reflect.full.isSubtypeOf
@@ -95,7 +94,14 @@ internal class Integration(
9594
applyRowsLimit = false
9695
)
9796

98-
render<HtmlData> { notebook.renderHtmlAsIFrameIfNeeded(it) }
97+
render<DataFrameHtmlData> {
98+
if (notebook.jupyterClientType == JupyterClientType.KOTLIN_NOTEBOOK) {
99+
(it.withTableDefinitions().toJupyterHtmlData()).toIFrame(notebook.currentColorScheme)
100+
} else {
101+
it.toJupyterHtmlData().toSimpleHtml(notebook.currentColorScheme)
102+
}
103+
}
104+
99105
render<AnyRow>(
100106
{ "DataRow: index = ${it.index()}, columnsCount = ${it.columnsCount()}" },
101107
)

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterHtmlRenderer.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import com.beust.klaxon.json
44
import org.jetbrains.kotlinx.dataframe.api.rows
55
import org.jetbrains.kotlinx.dataframe.api.toDataFrame
66
import org.jetbrains.kotlinx.dataframe.io.*
7-
import org.jetbrains.kotlinx.dataframe.io.initHtml
87
import org.jetbrains.kotlinx.dataframe.nrow
98
import org.jetbrains.kotlinx.dataframe.size
109
import org.jetbrains.kotlinx.jupyter.api.*
10+
import org.jetbrains.kotlinx.jupyter.api.HtmlData
1111
import org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration
1212

1313
/** Starting from this version, dataframe integration will respond with additional data for rendering in Kotlin Notebooks plugin. */
@@ -35,15 +35,15 @@ internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
3535
df.nrow
3636
}
3737

38-
val html = df.toHTML(
39-
reifiedDisplayConfiguration,
40-
extraHtml = initHtml(
38+
val html = (
39+
DataFrameHtmlData.tableDefinitions(
4140
includeJs = reifiedDisplayConfiguration.isolatedOutputs,
42-
includeCss = true,
43-
useDarkColorScheme = reifiedDisplayConfiguration.useDarkColorScheme
44-
),
45-
contextRenderer
46-
) { footer }
41+
includeCss = true
42+
) + df.toHTML(
43+
reifiedDisplayConfiguration,
44+
contextRenderer
45+
) { footer }
46+
).toJupyterHtmlData()
4747

4848
if (notebook.kernelVersion >= KotlinKernelVersion.from(MIN_KERNEL_VERSION_FOR_NEW_TABLES_UI)!!) {
4949
val jsonEncodedDf = json {
@@ -72,3 +72,5 @@ internal fun Notebook.renderAsIFrameAsNeeded(data: HtmlData, jsonEncodedDf: Stri
7272
"application/kotlindataframe+json" to jsonEncodedDf
7373
).also { it.isolatedHtml = false }
7474
}
75+
76+
internal fun DataFrameHtmlData.toJupyterHtmlData() = HtmlData(style, body, script)
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
package org.jetbrains.kotlinx.dataframe.rendering.html
22

33
import org.jetbrains.kotlinx.dataframe.AnyFrame
4-
import org.jetbrains.kotlinx.dataframe.io.initHtml
5-
import org.jetbrains.kotlinx.dataframe.io.toHTML
6-
import java.awt.Desktop
7-
import java.io.File
4+
import org.jetbrains.kotlinx.dataframe.io.toStandaloneHTML
85

96
fun AnyFrame.browse() {
10-
val file = File("temp.html") // File.createTempFile("df_rendering", ".html")
11-
file.writeText(toHTML(extraHtml = initHtml()).toString())
12-
val uri = file.toURI()
13-
val desktop = Desktop.getDesktop()
14-
desktop.browse(uri)
7+
toStandaloneHTML().openInBrowser()
158
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.jetbrains.kotlinx.dataframe.samples.api
2+
3+
import java.io.File
4+
import kotlin.io.path.Path
5+
import org.jetbrains.kotlinx.dataframe.api.reorderColumnsByName
6+
import org.jetbrains.kotlinx.dataframe.api.sortBy
7+
import org.jetbrains.kotlinx.dataframe.api.sortByDesc
8+
import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
9+
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
10+
import org.jetbrains.kotlinx.dataframe.io.toHTML
11+
import org.jetbrains.kotlinx.dataframe.io.toStandaloneHTML
12+
import org.junit.Ignore
13+
import org.junit.Test
14+
15+
class Render : TestBase() {
16+
@Test
17+
@Ignore
18+
fun useRenderingResult() {
19+
// SampleStart
20+
df.toStandaloneHTML(DisplayConfiguration(rowsLimit = null)).openInBrowser()
21+
df.toStandaloneHTML(DisplayConfiguration(rowsLimit = null)).writeHTML(File("/path/to/file"))
22+
df.toStandaloneHTML(DisplayConfiguration(rowsLimit = null)).writeHTML(Path("/path/to/file"))
23+
// SampleEnd
24+
}
25+
26+
@Test
27+
fun composeTables() {
28+
// SampleStart
29+
val df1 = df.reorderColumnsByName()
30+
val df2 = df.sortBy { age }
31+
val df3 = df.sortByDesc { age }
32+
33+
listOf(df1, df2, df3).fold(DataFrameHtmlData.tableDefinitions()) { acc, df -> acc + df.toHTML() }
34+
// SampleEnd
35+
}
36+
37+
@Test
38+
fun configureCellOutput() {
39+
// SampleStart
40+
df.toHTML(DisplayConfiguration(cellContentLimit = -1))
41+
// SampleEnd
42+
}
43+
}

core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/testSets/person/HtmlRenderingTests.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import org.jetbrains.kotlinx.dataframe.api.dataFrameOf
77
import org.jetbrains.kotlinx.dataframe.api.group
88
import org.jetbrains.kotlinx.dataframe.api.into
99
import org.jetbrains.kotlinx.dataframe.api.parse
10-
import org.jetbrains.kotlinx.dataframe.io.html
11-
import org.jetbrains.kotlinx.dataframe.io.initHtml
12-
import org.jetbrains.kotlinx.dataframe.io.toHTML
10+
import org.jetbrains.kotlinx.dataframe.io.toStandaloneHTML
1311
import org.jetbrains.kotlinx.jupyter.findNthSubstring
1412
import org.junit.Ignore
1513
import org.junit.Test
@@ -20,7 +18,7 @@ class HtmlRenderingTests : BaseTest() {
2018

2119
fun AnyFrame.browse() {
2220
val file = File("temp.html") // File.createTempFile("df_rendering", ".html")
23-
file.writeText(toHTML(extraHtml = initHtml()).toString())
21+
file.writeText(toStandaloneHTML().toString())
2422
val uri = file.toURI()
2523
val desktop = Desktop.getDesktop()
2624
desktop.browse(uri)
@@ -36,7 +34,7 @@ class HtmlRenderingTests : BaseTest() {
3634
fun `render url`() {
3735
val address = "http://www.google.com"
3836
val df = dataFrameOf("url")(address).parse()
39-
val html = df.html()
37+
val html = df.toStandaloneHTML().toString()
4038
html shouldContain "href"
4139
html.findNthSubstring(address, 2) shouldNotBe -1
4240
}

0 commit comments

Comments
 (0)