Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
Add Spring Boot DataFrame example with CSV data sources and web integ…
…ration

Introduce a comprehensive Spring Boot example (`springboot-dataframe-web`) showcasing annotated CSV-based data source initialization, web controllers, Thymeleaf templates, and sample data files. The example includes customer and sales reports with sorting and filtering functionalities, leveraging DataFrame operations and Spring Boot features.
  • Loading branch information
zaleslaw committed Aug 22, 2025
commit 833eefe22961ffdba5c1eb70a59fc0b88908da49
31 changes: 31 additions & 0 deletions examples/idea-examples/springboot-dataframe-web/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm")
id("org.springframework.boot") version "3.3.2"
id("io.spring.dependency-management") version "1.1.6"
application
}

repositories {
mavenCentral()
mavenLocal() // in case of local dataframe development
}

application {
mainClass.set("org.jetbrains.kotlinx.dataframe.examples.springboot.SpringbootDataframeApplicationKt")
}

dependencies {
implementation(project(":dataframe-spring"))
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
runtimeOnly("org.springframework.boot:spring-boot-devtools")
}

java.sourceCompatibility = JavaVersion.VERSION_17

tasks.withType<KotlinCompile> {
compilerOptions.jvmTarget = JvmTarget.JVM_17
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.jetbrains.kotlinx.dataframe.examples.springboot

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class SpringbootDataframeApplication

fun main(args: Array<String>) {
runApplication<SpringbootDataframeApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.jetbrains.kotlinx.dataframe.examples.springboot.config

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.spring.annotations.CsvDataSource
import org.springframework.stereotype.Component

@Component
class DataSources {
@CsvDataSource(file = "data/customers.csv")
lateinit var customers: DataFrame<*>

@CsvDataSource(file = "data/sales.csv")
lateinit var sales: DataFrame<*>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.jetbrains.kotlinx.dataframe.examples.springboot.service

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.api.*
import org.jetbrains.kotlinx.dataframe.examples.springboot.config.DataSources
import org.springframework.stereotype.Service

@Service
class ReportService(
private val dataSources: DataSources
) {
fun customersSortedByName(): DataFrame<*> =
dataSources.customers.sortBy("name")

fun customersFilteredByCountry(country: String): DataFrame<*> =
dataSources.customers.filter { it["country"].toString().equals(country, ignoreCase = true) }

fun salesSortedByValueDesc(): DataFrame<*> =
dataSources.sales.sortByDesc("value")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.jetbrains.kotlinx.dataframe.examples.springboot.web

import org.jetbrains.kotlinx.dataframe.examples.springboot.service.ReportService
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam

@Controller
class ReportController(
private val reportService: ReportService
) {
@GetMapping("/")
fun index(): String = "index"

@GetMapping("/customers")
fun customers(model: Model): String {
val df = reportService.customersSortedByName()
model.addAttribute("table", df.toTableView())
model.addAttribute("title", "Customers (sorted by name)")
return "table"
}

@GetMapping("/customers/filter")
fun customersFilter(
@RequestParam("country") country: String,
model: Model
): String {
val df = reportService.customersFilteredByCountry(country)
model.addAttribute("table", df.toTableView())
model.addAttribute("title", "Customers from $country")
return "table"
}

@GetMapping("/sales")
fun sales(model: Model): String {
val df = reportService.salesSortedByValueDesc()
model.addAttribute("table", df.toTableView())
model.addAttribute("title", "Sales (sorted by value desc)")
return "table"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.jetbrains.kotlinx.dataframe.examples.springboot.web

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.api.*

data class TableView(
val headers: List<String>,
val rows: List<List<String>>
)

fun DataFrame<*>.toTableView(): TableView {
val headers = this.columnNames()
val rows = this.rows().map { row -> headers.map { h -> row[h].toString() } }
return TableView(headers, rows)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spring.thymeleaf.cache=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
id,name,country,email
1,Alice Johnson,USA,[email protected]
2,Bob Smith,Canada,[email protected]
3,Charlie Davis,USA,[email protected]
4,Diana Evans,UK,[email protected]
5,Edward Wilson,USA,[email protected]
6,Fiona Brown,Australia,[email protected]
7,George Miller,Germany,[email protected]
8,Helen Clark,USA,[email protected]
9,Ian Thompson,Ireland,[email protected]
10,Julia Roberts,USA,[email protected]
11,Kevin Lee,Canada,[email protected]
12,Linda Perez,Spain,[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sale_id,customer_id,product,value,date
1001,1,Laptop,1200.50,2025-01-05
1002,2,Phone,799.99,2025-01-10
1003,3,Tablet,450.00,2025-02-14
1004,4,Headphones,149.99,2025-02-20
1005,5,Monitor,299.49,2025-03-01
1006,6,Keyboard,89.99,2025-03-12
1007,7,Mouse,49.95,2025-03-15
1008,8,Smartwatch,199.00,2025-04-01
1009,9,Camera,650.75,2025-04-12
1010,10,Printer,220.00,2025-04-20
1011,11,Speaker,130.00,2025-05-02
1012,12,Router,99.99,2025-05-10
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>DataFrame Spring Boot Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 2rem; }
.btn { display: inline-block; margin: .25rem; padding: .5rem 1rem; background: #1e88e5; color: #fff; text-decoration: none; border-radius: 4px; }
.btn.secondary { background: #43a047; }
.btn.warn { background: #fb8c00; }
</style>
</head>
<body>
<h1>Reports</h1>
<p>Choose one of the reports below:</p>
<div>
<a class="btn" href="/customers">Customers (sort by name)</a>
<a class="btn" href="/sales">Sales (sort by value desc)</a>
</div>
<hr/>
<h2>Filter Customers by Country</h2>
<form action="/customers/filter" method="get">
<label>Country: <input type="text" name="country" placeholder="USA" required/></label>
<button class="btn secondary" type="submit">Filter</button>
</form>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title th:text="${title}">Table</title>
<style>
body { font-family: Arial, sans-serif; margin: 2rem; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background: #f5f5f5; text-align: left; }
.btn { display: inline-block; margin: .25rem; padding: .5rem 1rem; background: #1e88e5; color: #fff; text-decoration: none; border-radius: 4px; }
</style>
</head>
<body>
<a class="btn" href="/">Back</a>
<h2 th:text="${title}">Table</h2>
<table>
<thead>
<tr>
<th th:each="h : ${table.headers}" th:text="${h}">Header</th>
</tr>
</thead>
<tbody>
<tr th:each="row : ${table.rows}">
<td th:each="cell : ${row}" th:text="${cell}">cell</td>
</tr>
</tbody>
</table>
</body>
</html>
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ include("examples:idea-examples:movies")
include("examples:idea-examples:youtube")
include("examples:idea-examples:json")
include("examples:idea-examples:unsupported-data-sources")
include("examples:idea-examples:springboot-dataframe-web")
includeBuild("examples/kotlin-dataframe-plugin-example")

val jupyterApiTCRepo: String by settings
Expand Down