From d1d53227bb7c47cf0641b427f4c051acf3c236f1 Mon Sep 17 00:00:00 2001 From: Tom Cools Date: Thu, 25 Jun 2026 14:39:35 +0200 Subject: [PATCH 1/5] chore: add kotlin code for service docs. --- .../pages/service/constraint-weights.adoc | 92 +++++++++++ .../modules/ROOT/pages/service/demo-data.adoc | 52 +++++++ .../ROOT/pages/service/exposing-metrics.adoc | 143 ++++++++++++++++++ .../ROOT/pages/service/modeling-changes.adoc | 115 +++++++++++++- .../modules/ROOT/pages/service/rest-api.adoc | 136 +++++++++++++++++ 5 files changed, 536 insertions(+), 2 deletions(-) diff --git a/docs/src/modules/ROOT/pages/service/constraint-weights.adoc b/docs/src/modules/ROOT/pages/service/constraint-weights.adoc index 2c0d1ed2a4..10664c56ee 100644 --- a/docs/src/modules/ROOT/pages/service/constraint-weights.adoc +++ b/docs/src/modules/ROOT/pages/service/constraint-weights.adoc @@ -22,6 +22,11 @@ The implementation should have fields that refer to specific constraints using t To ensure both the constraint and this reference are the same, use a static field to keep the name of the constraint. .The example ConstraintProvider class. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- public class TimetableConstraintProvider implements ConstraintProvider { @@ -44,8 +49,44 @@ public class TimetableConstraintProvider implements ConstraintProvider { // other constraints excluded } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +class TimetableConstraintProvider : ConstraintProvider { + + companion object { + const val TEACHER_CONFLICT = "Teacher conflict" + const val ROOM_CONFLICT = "Room conflict" + } + + fun roomConflict(constraintFactory: ConstraintFactory): Constraint { + return constraintFactory + // constraint implementation excluded + .asConstraint(ROOM_CONFLICT) + } + + fun teacherConflict(constraintFactory: ConstraintFactory): Constraint { + return constraintFactory + // constraint implementation excluded + .asConstraint(TEACHER_CONFLICT) + } + + // other constraints excluded +} +---- +-- +==== .The ModelConfigOverrides class. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- public final class TimetableConfigOverrides implements ModelConfigOverrides { @@ -63,6 +104,30 @@ public final class TimetableConfigOverrides implements ModelConfigOverrides { } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +class TimetableConfigOverrides : ModelConfigOverrides { + + companion object { + const val DEFAULT_WEIGHT_ZERO = 0L + const val DEFAULT_WEIGHT_ONE = 1L + } + + @ConstraintReference(TimetableConstraintProvider.TEACHER_CONFLICT) + var teacherConflictWeight: Long = DEFAULT_WEIGHT_ONE + + @ConstraintReference(TimetableConstraintProvider.ROOM_CONFLICT) + var roomConflictWeight: Long = DEFAULT_WEIGHT_ONE + +} +---- +-- +==== The default constraint weight for these constraints is `1`. This can now be overridden by the consumer by passing in the model overrides object in a request. For example, to make the Teacher conflict 10 times more impactful, override the weight to 10: @@ -93,6 +158,11 @@ Usually, it doesn't make sense to allow weight overrides for _hard_ constraints. Next, in the xref:./rest-api.adoc#modelConverter[model converter], make sure to map these overrides to a solver specific `ConstraintWeightOverrides` object that must be on the `@PlanningSolution` class. .As part of the ModelConvertor +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- TimetableConfigOverrides modelConfigOverrides = modelConfig.overrides(); @@ -108,6 +178,28 @@ ConstraintWeightOverrides constraintWeightOverrides = C solverModel.setConstraintWeightOverrides(constraintWeightOverrides); ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +val modelConfigOverrides = modelConfig.overrides() + +val constraintWeightOverrides = ConstraintWeightOverrides.of( + mapOf( + TimetableConstraintProvider.TEACHER_CONFLICT to + HardMediumSoftLongScore.ofHard(modelConfigOverrides.teacherConflictWeight), + TimetableConstraintProvider.ROOM_CONFLICT to + HardMediumSoftLongScore.ofSoft(modelConfigOverrides.roomConflictWeight) + ) +) + +solverModel.constraintWeightOverrides = constraintWeightOverrides +---- +-- +==== For more information, see xref:../constraints-and-score/constraint-configuration.adoc#constraintConfiguration[Adjusting constraints at runtime]. diff --git a/docs/src/modules/ROOT/pages/service/demo-data.adoc b/docs/src/modules/ROOT/pages/service/demo-data.adoc index b798b70344..095aca2b2f 100644 --- a/docs/src/modules/ROOT/pages/service/demo-data.adoc +++ b/docs/src/modules/ROOT/pages/service/demo-data.adoc @@ -23,6 +23,11 @@ This interface requires you to implement 2 methods: Implementations of this interface must be dependency free meaning simple instantiation (even with reflection) of this class is enough to generate demo data. ==== +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @ApplicationScoped @@ -79,6 +84,53 @@ public class TimetableDemoDataGenerator implements DemoDataGenerator { } } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@ApplicationScoped +class TimetableDemoDataGenerator : DemoDataGenerator { + + enum class DemoDataKind( + private val metaData: DemoMetaData, + private val requestFunction: (DemoDataKind) -> ModelRequest + ) { + BASIC( + DemoMetaData("BASIC", "SHORT_DESCRIPTION", "LONG_DESCRIPTION", listOf("TAGS"), listOf()), + { it.generateBasicDemoData() } // could also delegate to another class instead + ), + COMPLEX_SET( + DemoMetaData("COMPLEX_SET", "SHORT_DESCRIPTION", "LONG_DESCRIPTION", listOf("TAGS"), listOf()), + { it.generateComplexSet() } + ); + + fun getMetaData(): DemoMetaData = metaData + + fun getDemoData(): DemoData = DemoData(metaData, requestFunction(this)) + + fun generateBasicDemoData(): ModelRequest { + // Generate basic request. + } + + fun generateComplexSet(): ModelRequest { + // Generate complex request. + } + } + + override fun demoMetaData(): List { + return DemoDataKind.entries.map { it.getMetaData() } + } + + override fun generateDemoData(demoDataId: String): DemoData { + return DemoDataKind.fromString(demoDataId).getDemoData() + } +} +---- +-- +==== With this interface implemented, Timefold Solver will automatically expose these methods as REST endpoints: diff --git a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc index 1dd8659cb7..54811ee624 100644 --- a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc +++ b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc @@ -22,6 +22,11 @@ It is therefore necessary to add xref:./rest-api.adoc#openAPISpecification[OpenA ==== .Example for School Timetabling +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- public record TimetableInputMetrics( @@ -33,10 +38,33 @@ public record TimetableInputMetrics( type = SchemaType.NUMBER, example = "30", readOnly = true) int timeslots ) implements ModelInputMetrics {} ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +data class TimetableInputMetrics( + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) @Schema(name = "lessons", title = "Lessons", + format = DataFormat.Values.NUMBER, description = "The number of lessons submitted in the input dataset.", + type = SchemaType.NUMBER, example = "10", readOnly = true) val lessons: Int, + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) @Schema(name = "timeslots", title = "Timeslots", + format = DataFormat.Values.NUMBER, description = "The number of timeslots submitted in the input dataset.", + type = SchemaType.NUMBER, example = "30", readOnly = true) val timeslots: Int +) : ModelInputMetrics +---- +-- +==== Next, the `SolverModel` should implement the `InputMetricsAware` interface and construct the defined `ModelInputMetrics` object. .Example for School Timetabling +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @PlanningSolution @@ -65,6 +93,37 @@ public class Timetable implements SolverModel, InputMetricsAware< // other Getters/Setters/Constructors excluded } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@PlanningSolution +class Timetable : SolverModel, InputMetricsAware { + + @ProblemFactCollectionProperty + @ValueRangeProvider + var timeslots: List = emptyList() + + @PlanningEntityCollectionProperty + var lessons: List = emptyList() + + @PlanningScore + var score: HardSoftScore? = null + + override fun getScore(): HardSoftScore? = score + + override fun getInputMetrics(): TimetableInputMetrics { + return TimetableInputMetrics(lessons.size, timeslots.size) + } + + // other Getters/Setters/Constructors excluded +} +---- +-- +==== [#modelOutputMetrics] == Output metrics @@ -82,6 +141,11 @@ It is therefore necessary to add xref:./rest-api.adoc#openAPISpecification[OpenA ==== .Example for School Timetabling +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- public record TimetableOutputMetrics( @@ -93,10 +157,33 @@ public record TimetableOutputMetrics( type = SchemaType.NUMBER, example = "3", readOnly = true) int maxConsecutiveLessons ) implements ModelOutputMetrics {} ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +data class TimetableOutputMetrics( + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) @Schema(name = "unassignedLessons", title = "Unassigned lessons", + format = DataFormat.Values.NUMBER, description = "The number of lessons that could not be assigned a timeslot or room.", + type = SchemaType.NUMBER, example = "0", readOnly = true) val unassignedLessons: Int, + @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT) @Schema(name = "maxConsecutiveLessons", title = "Max consecutive lessons", + format = DataFormat.Values.NUMBER, description = "The maximum number of consecutive lessons assigned to any single teacher.", + type = SchemaType.NUMBER, example = "3", readOnly = true) val maxConsecutiveLessons: Int +) : ModelOutputMetrics +---- +-- +==== Next, the `SolverModel` should implement the `OutputMetricsAware` interface and construct the defined `ModelOutputMetrics` object from the solved state. .Example for School Timetabling +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @PlanningSolution @@ -129,12 +216,50 @@ public class Timetable implements SolverModel, OutputMetricsAware // other Getters/Setters/Constructors excluded } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@PlanningSolution +class Timetable : SolverModel, OutputMetricsAware { + + @ProblemFactCollectionProperty + @ValueRangeProvider + var timeslots: List = emptyList() + + @PlanningEntityCollectionProperty + var lessons: List = emptyList() + + @PlanningScore + var score: HardSoftScore? = null + + override fun getScore(): HardSoftScore? = score + + override fun getOutputMetrics(): TimetableOutputMetrics { + val unassigned = lessons.count { it.timeslot == null || it.room == null } + val maxConsecutive = computeMaxConsecutiveLessons(lessons) + return TimetableOutputMetrics(unassigned, maxConsecutive) + } + + // other Getters/Setters/Constructors excluded +} +---- +-- +==== [#combiningMetrics] == Combining input and output metrics A `SolverModel` can implement both `InputMetricsAware` and `OutputMetricsAware` at the same time. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @PlanningSolution @@ -146,3 +271,21 @@ public class Timetable implements SolverModel, } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@PlanningSolution +class Timetable : SolverModel, + InputMetricsAware, + OutputMetricsAware { + + // fields, getInputMetrics(), getOutputMetrics() excluded + +} +---- +-- +==== diff --git a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc index 28ecb48f94..af1b1bdedf 100644 --- a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc +++ b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc @@ -14,6 +14,11 @@ include::_preview-note.adoc[] Your class which is annotated by `@PlanningSolution` should implement the `SolverModel` interface. .Example for School Timetabling +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @PlanningSolution @@ -37,6 +42,33 @@ public class Timetable implements SolverModel { // other Getters/Setters/Constructors excluded } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@PlanningSolution +class Timetable : SolverModel { + + @ProblemFactCollectionProperty + @ValueRangeProvider + var timeslots: List = emptyList() + + @PlanningEntityCollectionProperty + var lessons: List = emptyList() + + @PlanningScore + var score: HardSoftScore? = null + + override fun getScore(): HardSoftScore? = score + + // other Getters/Setters/Constructors excluded +} +---- +-- +==== In case you don't want control over the `score` class used, you can also extend the `AbstractSimpleModel` as used in the xref:../quickstart/service/getting-started.adoc[getting started guide]. @@ -53,9 +85,13 @@ Especially when the field depends on external information or is difficult to com In this example, we enrich the Timetable PlanningSolution described above by filling in the "isHoliday" field for all Timeslot objects. .Timeslot class for the School Timetabling example +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- - public class Timeslot { private LocalDateTime startTime; @@ -70,10 +106,34 @@ public class Timeslot { // other Getters/Setters/Constructors excluded } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +class Timeslot { + + var startTime: LocalDateTime? = null + var endTime: LocalDateTime? = null + + var isHoliday: Boolean = false + + // other Getters/Setters/Constructors excluded +} +---- +-- +==== Enrichment of the SolverModel is possible by implementing a `SolverModelEnricher`. .Timeslot Enricher for the School Timetabling example +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @ApplicationScoped @@ -94,8 +154,33 @@ public class TimeslotHolidayEnricher implements SolverModelEnricher { return false; } } +---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@ApplicationScoped +class TimeslotHolidayEnricher : SolverModelEnricher { + override fun enrich(solverModel: Timetable): Timetable { + for (timeslot in solverModel.timeslots) { + timeslot.isHoliday = overlapsWithKnownHoliday(timeslot.startTime, timeslot.endTime) + } + + return solverModel + } + + private fun overlapsWithKnownHoliday(start: LocalDateTime?, end: LocalDateTime?): Boolean { + // Implementation excluded; potentially call external service / database. + return false + } +} ---- +-- +==== Next, register a `SolverModelEnrichmentDirector` implementation. This class allows you to determine the order in which enrichers are executed. @@ -103,6 +188,11 @@ This might be important when 1 of your custom enrichers depends on an enricher p //TODO add link to maps component docs https://github.com/TimefoldAI/timefold-solver/issues/2348 .Timetable Enrichment Director for the School Timetabling example +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @ApplicationScoped @@ -122,4 +212,25 @@ public class TimetableEnrichmentDirector implements SolverModelEnrichmentDirecto return enrichedModel; } } ----- \ No newline at end of file +---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@ApplicationScoped +class TimetableEnrichmentDirector @Inject constructor( + private val timeslotEnricher: TimeslotHolidayEnricher +) : SolverModelEnrichmentDirector { + + override fun enrich(solverModel: Timetable): Timetable { + val enrichedModel = timeslotEnricher.enrich(solverModel) + // Additional enrichers. + return enrichedModel + } +} +---- +-- +==== \ No newline at end of file diff --git a/docs/src/modules/ROOT/pages/service/rest-api.adoc b/docs/src/modules/ROOT/pages/service/rest-api.adoc index 639d5bfa6c..5c8d68e3dc 100644 --- a/docs/src/modules/ROOT/pages/service/rest-api.adoc +++ b/docs/src/modules/ROOT/pages/service/rest-api.adoc @@ -18,6 +18,11 @@ The following are expected. - An interface which extends `ModelRest` to define the root path of the REST API. For example, in the case of School Timetabling, those classes could look as follows. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- // TimetableDto.java @@ -40,6 +45,31 @@ public class TimetableDto implements ModelInput, ModelOutput { public interface TimetableSchedulingResource extends ModelRest { } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +// TimetableDto.kt +class TimetableDto( + @Schema(required = true, description = "The unique identifier of timetable") + var name: String? = null, + @Schema(required = true, description = "List of timeslots") + var timeslots: List = emptyList(), + @Schema(required = true, description = "List of lessons") + var lessons: List = emptyList() +) : ModelInput, ModelOutput + +// TimetableSchedulingResource.kt +@Tag(name = "School Timetabling", + description = "School timetabling service assigning lessons to timeslots.") // OpenAPI documentation annotation +@Path("/v1/timetables") //sets the root path +interface TimetableSchedulingResource : ModelRest +---- +-- +==== Note how the `ModelInput` and `ModelOutput` interface can be placed on the same class. @@ -199,6 +229,11 @@ it's often beneficial to decouple the classes used for the REST API interactions To convert between `ModelInput` / `ModelOutput` and the `SolverModel`, a `ModelConvertor` implementation can be provided. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @ApplicationScoped @@ -221,6 +256,32 @@ public class TimetableConvertor implements ModelConvertor { + + override fun toSolverModel(modelInput: TimetableDto, modelConfig: ModelConfig, + lastModelOutput: Optional): Timetable { + return // Mapping logic + } + + override fun toModelOutput(solverModel: Timetable): TimetableDto { + return // Mapping logic + } + + override fun applyOutputToInput(modelInput: TimetableDto, modelOutput: TimetableDto): TimetableDto { + return // Mapping logic + } +} +---- +-- +==== [NOTE] ==== @@ -235,6 +296,11 @@ By default, validation on the input is based on the xref:./rest-api.adoc#openAPI Additional validations can be added by implementing the `ModelValidator` interface. [#timetableValidatorExample] +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @ApplicationScoped @@ -269,6 +335,39 @@ public enum TimetableValidationIssue { } } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@ApplicationScoped +class TimetableValidator : ModelValidator { + + override fun validate(validationBuilder: ValidationBuilder, input: TimetableDto, modelConfig: ModelConfig) { + + //validation logic here, simplified example here. + if (hasDuplicateTeacherNames(input)) { + validationBuilder.addIssue(TimetableValidationIssue.DUPLICATE_TEACHER.asIssueType(), DuplicateTeacherDetail("Ann")) + } + } +} + +enum class TimetableValidationIssue( + private val issueType: IssueType +) { + + DUPLICATE_TEACHER(IssueType(IssueCode.of("DUPLICATE_TEACHER"), IssueSeverity.ERROR, + "Duplicate teacher names found.")); + + fun asIssueType(): IssueType = issueType + + data class DuplicateTeacherDetail(val teacherName: String) : IssueDetail +} +---- +-- +==== The `ValidationBuilder` supports issue types of different severity: - `IssueSeverity.ERROR`: error level: processing cannot continue. Results in a BAD_REQUEST (400) HTTP error. @@ -338,6 +437,11 @@ You can add custom endpoints in the `ModelRest` implementation. For example, if you want to extend the Timetable REST API, you can add custom https://jakarta.ee/learn/docs/jakartaee-tutorial/current/websvcs/rest/rest.html[Jakarta REST endpoints]. These endpoints should also be documented appropriately for the automatically generated xref:#openAPISpecification[OpenAPI specification]. +[tabs] +==== +Java:: ++ +-- [source,java,options="nowrap"] ---- @Tag(name = "School Timetabling", @@ -363,6 +467,38 @@ public interface TimetableSchedulingResource extends ModelRest { } } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,options="nowrap"] +---- +@Tag(name = "School Timetabling", + description = "School timetabling service assigning lessons to timeslots.") //OpenAPI documentation annotation +@Path("/v1/timetables") //sets the root path +interface TimetableSchedulingResource : ModelRest { + + @APIResponses(value = [ + APIResponse(responseCode = "500", description = "In case of processing errors", + content = [Content(mediaType = MediaType.APPLICATION_JSON, + schema = Schema(implementation = ErrorInfo::class))]), + APIResponse(responseCode = "200", description = "List of all teachers in all optimization runs.", + content = [Content(mediaType = MediaType.APPLICATION_JSON, + schema = Schema(implementation = ListTeachersResponseDto::class))])]) + @Operation(operationId = "list-teachers-in-solver-model", + summary = "Lists all teachers in the solver model.") + @GET + @Path("/insights/teachers") + @Produces(MediaType.APPLICATION_JSON) + fun insightsTeachers(): Response { + val teachers = //get the list of teachers + return Response.ok(teachers).build() + } +} +---- +-- +==== [#openAPISpecification] == OpenAPI specification From 6526f83b8b7f7da3038733e29258cb8efc37ee40 Mon Sep 17 00:00:00 2001 From: Tom Cools Date: Thu, 25 Jun 2026 22:53:36 +0200 Subject: [PATCH 2/5] docs: review comments --- .../ROOT/pages/service/constraint-weights.adoc | 13 ++++++------- .../src/modules/ROOT/pages/service/demo-data.adoc | 4 ++-- .../ROOT/pages/service/exposing-metrics.adoc | 8 ++++---- .../ROOT/pages/service/modeling-changes.adoc | 15 ++++++--------- docs/src/modules/ROOT/pages/service/rest-api.adoc | 10 +++++----- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/docs/src/modules/ROOT/pages/service/constraint-weights.adoc b/docs/src/modules/ROOT/pages/service/constraint-weights.adoc index 10664c56ee..70429c0036 100644 --- a/docs/src/modules/ROOT/pages/service/constraint-weights.adoc +++ b/docs/src/modules/ROOT/pages/service/constraint-weights.adoc @@ -111,19 +111,18 @@ Kotlin:: -- [source,kotlin,options="nowrap"] ---- -class TimetableConfigOverrides : ModelConfigOverrides { +data class TimetableConfigOverrides( + @ConstraintReference(TimetableConstraintProvider.TEACHER_CONFLICT) + val teacherConflictWeight: Long = DEFAULT_WEIGHT_ONE, + @ConstraintReference(TimetableConstraintProvider.ROOM_CONFLICT) + val roomConflictWeight: Long = DEFAULT_WEIGHT_ONE +) : ModelConfigOverrides { companion object { const val DEFAULT_WEIGHT_ZERO = 0L const val DEFAULT_WEIGHT_ONE = 1L } - @ConstraintReference(TimetableConstraintProvider.TEACHER_CONFLICT) - var teacherConflictWeight: Long = DEFAULT_WEIGHT_ONE - - @ConstraintReference(TimetableConstraintProvider.ROOM_CONFLICT) - var roomConflictWeight: Long = DEFAULT_WEIGHT_ONE - } ---- -- diff --git a/docs/src/modules/ROOT/pages/service/demo-data.adoc b/docs/src/modules/ROOT/pages/service/demo-data.adoc index 095aca2b2f..e32e6ee85e 100644 --- a/docs/src/modules/ROOT/pages/service/demo-data.adoc +++ b/docs/src/modules/ROOT/pages/service/demo-data.adoc @@ -112,11 +112,11 @@ class TimetableDemoDataGenerator : DemoDataGenerator { fun getDemoData(): DemoData = DemoData(metaData, requestFunction(this)) fun generateBasicDemoData(): ModelRequest { - // Generate basic request. + return TODO("Generate basic request.") } fun generateComplexSet(): ModelRequest { - // Generate complex request. + return TODO("Generate complex request.") } } diff --git a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc index 54811ee624..c1b3e6007f 100644 --- a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc +++ b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc @@ -105,10 +105,10 @@ class Timetable : SolverModel, InputMetricsAware = emptyList() + val timeslots: List = emptyList() @PlanningEntityCollectionProperty - var lessons: List = emptyList() + val lessons: List = emptyList() @PlanningScore var score: HardSoftScore? = null @@ -228,10 +228,10 @@ class Timetable : SolverModel, OutputMetricsAware = emptyList() + val timeslots: List = emptyList() @PlanningEntityCollectionProperty - var lessons: List = emptyList() + val lessons: List = emptyList() @PlanningScore var score: HardSoftScore? = null diff --git a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc index af1b1bdedf..4e126e51f7 100644 --- a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc +++ b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc @@ -54,10 +54,10 @@ class Timetable : SolverModel { @ProblemFactCollectionProperty @ValueRangeProvider - var timeslots: List = emptyList() + val timeslots: List = emptyList() @PlanningEntityCollectionProperty - var lessons: List = emptyList() + val lessons: List = emptyList() @PlanningScore var score: HardSoftScore? = null @@ -113,14 +113,11 @@ Kotlin:: -- [source,kotlin,options="nowrap"] ---- -class Timeslot { - - var startTime: LocalDateTime? = null - var endTime: LocalDateTime? = null - +data class Timeslot( + val startTime: LocalDateTime? = null, + val endTime: LocalDateTime? = null +) { var isHoliday: Boolean = false - - // other Getters/Setters/Constructors excluded } ---- -- diff --git a/docs/src/modules/ROOT/pages/service/rest-api.adoc b/docs/src/modules/ROOT/pages/service/rest-api.adoc index 5c8d68e3dc..3f3c5c4031 100644 --- a/docs/src/modules/ROOT/pages/service/rest-api.adoc +++ b/docs/src/modules/ROOT/pages/service/rest-api.adoc @@ -268,15 +268,15 @@ class TimetableConvertor : ModelConvertor, lastModelOutput: Optional): Timetable { - return // Mapping logic + return TODO("Mapping logic") } override fun toModelOutput(solverModel: Timetable): TimetableDto { - return // Mapping logic + return TODO("Mapping logic") } override fun applyOutputToInput(modelInput: TimetableDto, modelOutput: TimetableDto): TimetableDto { - return // Mapping logic + return TODO("Mapping logic") } } ---- @@ -349,7 +349,7 @@ class TimetableValidator : ModelValidator Date: Thu, 25 Jun 2026 23:05:54 +0200 Subject: [PATCH 3/5] docs: review comments --- docs/src/modules/ROOT/pages/service/rest-api.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/modules/ROOT/pages/service/rest-api.adoc b/docs/src/modules/ROOT/pages/service/rest-api.adoc index 3f3c5c4031..cb028e83fd 100644 --- a/docs/src/modules/ROOT/pages/service/rest-api.adoc +++ b/docs/src/modules/ROOT/pages/service/rest-api.adoc @@ -349,7 +349,7 @@ class TimetableValidator : ModelValidator Date: Fri, 26 Jun 2026 09:55:53 +0200 Subject: [PATCH 4/5] docs: review comments --- docs/src/modules/ROOT/pages/service/exposing-metrics.adoc | 8 ++++---- docs/src/modules/ROOT/pages/service/modeling-changes.adoc | 4 ++-- docs/src/modules/ROOT/pages/service/rest-api.adoc | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc index c1b3e6007f..0d28766731 100644 --- a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc +++ b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc @@ -110,9 +110,9 @@ class Timetable : SolverModel, InputMetricsAware = emptyList() - @PlanningScore - var score: HardSoftScore? = null + private var score: HardSoftScore? = null + @PlanningScore override fun getScore(): HardSoftScore? = score override fun getInputMetrics(): TimetableInputMetrics { @@ -233,9 +233,9 @@ class Timetable : SolverModel, OutputMetricsAware = emptyList() - @PlanningScore - var score: HardSoftScore? = null + private var score: HardSoftScore? = null + @PlanningScore override fun getScore(): HardSoftScore? = score override fun getOutputMetrics(): TimetableOutputMetrics { diff --git a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc index 4e126e51f7..ae8ba8499a 100644 --- a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc +++ b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc @@ -59,9 +59,9 @@ class Timetable : SolverModel { @PlanningEntityCollectionProperty val lessons: List = emptyList() - @PlanningScore - var score: HardSoftScore? = null + private var score: HardSoftScore? = null + @PlanningScore override fun getScore(): HardSoftScore? = score // other Getters/Setters/Constructors excluded diff --git a/docs/src/modules/ROOT/pages/service/rest-api.adoc b/docs/src/modules/ROOT/pages/service/rest-api.adoc index cb028e83fd..9bece72bb2 100644 --- a/docs/src/modules/ROOT/pages/service/rest-api.adoc +++ b/docs/src/modules/ROOT/pages/service/rest-api.adoc @@ -55,7 +55,7 @@ Kotlin:: // TimetableDto.kt class TimetableDto( @Schema(required = true, description = "The unique identifier of timetable") - var name: String? = null, + var name: String = "", @Schema(required = true, description = "List of timeslots") var timeslots: List = emptyList(), @Schema(required = true, description = "List of lessons") From 1d4435f26c594c3632f956a9a792fe70e82a49f0 Mon Sep 17 00:00:00 2001 From: Tom Cools Date: Fri, 26 Jun 2026 12:15:08 +0200 Subject: [PATCH 5/5] docs: review comments --- docs/src/modules/ROOT/pages/service/exposing-metrics.adoc | 8 ++++---- docs/src/modules/ROOT/pages/service/modeling-changes.adoc | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc index 0d28766731..0c7388d0e2 100644 --- a/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc +++ b/docs/src/modules/ROOT/pages/service/exposing-metrics.adoc @@ -110,10 +110,10 @@ class Timetable : SolverModel, InputMetricsAware = emptyList() - private var score: HardSoftScore? = null + private var _score: HardSoftScore? = null @PlanningScore - override fun getScore(): HardSoftScore? = score + override fun getScore(): HardSoftScore? = _score override fun getInputMetrics(): TimetableInputMetrics { return TimetableInputMetrics(lessons.size, timeslots.size) @@ -233,10 +233,10 @@ class Timetable : SolverModel, OutputMetricsAware = emptyList() - private var score: HardSoftScore? = null + private var _score: HardSoftScore? = null @PlanningScore - override fun getScore(): HardSoftScore? = score + override fun getScore(): HardSoftScore? = _score override fun getOutputMetrics(): TimetableOutputMetrics { val unassigned = lessons.count { it.timeslot == null || it.room == null } diff --git a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc index ae8ba8499a..82e7c9421f 100644 --- a/docs/src/modules/ROOT/pages/service/modeling-changes.adoc +++ b/docs/src/modules/ROOT/pages/service/modeling-changes.adoc @@ -59,10 +59,10 @@ class Timetable : SolverModel { @PlanningEntityCollectionProperty val lessons: List = emptyList() - private var score: HardSoftScore? = null + private var _score: HardSoftScore? = null @PlanningScore - override fun getScore(): HardSoftScore? = score + override fun getScore(): HardSoftScore? = _score // other Getters/Setters/Constructors excluded }