Skip to content

Commit d5fb738

Browse files
committed
feat: add configurable scheduler interval and improve related documentation
1 parent 9d91aa3 commit d5fb738

9 files changed

Lines changed: 42 additions & 12 deletions

File tree

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ The **Automation** page allows you to create recurring calendar synchronization
102102
Current scheduler behavior:
103103

104104
- scheduled jobs process the **current day**;
105-
- all schedules run every **60 minutes**;
105+
- all schedules use the global scheduler interval, which defaults to **1 hour**;
106106
- schedules are persisted in SQLite and survive container restarts;
107107
- duplicate schedule definitions are rejected;
108108
- each schedule can be executed immediately with **Run now**;
@@ -224,12 +224,18 @@ services:
224224
environment:
225225
# Replace with your local IANA timezone.
226226
JAVA_TOOL_OPTIONS: "-Duser.timezone=Europe/Lisbon"
227+
# Global interval for all scheduled syncs. Accepted values: 1-24 hours.
228+
SCHEDULER_INTERVAL_HOURS: "${SCHEDULER_INTERVAL_HOURS:-1}"
227229
ports:
228230
- "8098:8080"
229231
volumes:
230232
- ./data:/data
231233
```
232234
235+
`SCHEDULER_INTERVAL_HOURS` is read when the application starts. It accepts whole
236+
hours from `1` to `24`, applies to every scheduled sync in the instance, and
237+
defaults to `1`. Recreate the container after changing it.
238+
233239
Start the application:
234240

235241
```bash
@@ -296,6 +302,7 @@ docker run --rm \
296302
--name tp2intervals \
297303
-p 8098:8080 \
298304
-e JAVA_TOOL_OPTIONS="-Duser.timezone=Europe/Lisbon" \
305+
-e SCHEDULER_INTERVAL_HOURS=1 \
299306
-v "$(pwd)/data:/data" \
300307
tp2intervals:local
301308
```
@@ -323,6 +330,7 @@ cd boot
323330
324331
SPRING_DATASOURCE_URL="jdbc:sqlite:../data/tp2intervals.sqlite" \
325332
LOGGING_FILE_NAME="../data/tp2intervals.log" \
333+
SCHEDULER_INTERVAL_HOURS=1 \
326334
./gradlew bootRun
327335
```
328336

@@ -424,8 +432,8 @@ The TrainingPeaks and TrainerRoad integrations depend on web endpoints and sessi
424432
- TrainerRoad is supported as a source, not as a synchronization destination.
425433
- Changed-workout replacement is currently limited to one-day TrainerRoad → TrainingPeaks operations.
426434
- Changed-workout detection is primarily based on the TrainerRoad workout identifier. A content change that keeps the same identifier may be treated as already synchronized.
427-
- Scheduled jobs always process the current day and currently run at a fixed 60-minute interval.
428-
- The scheduler interval cannot yet be configured per schedule.
435+
- Scheduled jobs always process the current day.
436+
- The scheduler interval is global for the application instance and cannot be configured per schedule.
429437
- Synchronization history currently covers calendar-to-calendar operations only.
430438
- TrainingPeaks capabilities can differ between athlete, coach, free, and Premium accounts.
431439

boot/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies {
3333
implementation("org.springframework.boot:spring-boot-starter-web")
3434
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
3535
implementation("org.springframework.boot:spring-boot-starter-actuator")
36+
implementation("org.springframework.boot:spring-boot-starter-validation")
3637

3738
implementation("org.hibernate.orm:hibernate-community-dialects:6.4.2.Final")
3839
implementation("org.xerial:sqlite-jdbc:3.45.0.0")

boot/src/main/kotlin/org/freekode/tp2intervals/Application.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.freekode.tp2intervals
22

33
import org.freekode.tp2intervals.infrastructure.configuration.DefaultConfiguration
4+
import org.freekode.tp2intervals.infrastructure.configuration.SchedulerProperties
45
import org.springframework.boot.autoconfigure.SpringBootApplication
56
import org.springframework.boot.context.properties.EnableConfigurationProperties
67
import org.springframework.boot.runApplication
@@ -12,7 +13,10 @@ import org.springframework.scheduling.annotation.EnableScheduling
1213
@EnableFeignClients
1314
@EnableCaching
1415
@EnableScheduling
15-
@EnableConfigurationProperties(DefaultConfiguration::class)
16+
@EnableConfigurationProperties(
17+
DefaultConfiguration::class,
18+
SchedulerProperties::class,
19+
)
1620
class Application
1721

1822
fun main(args: Array<String>) {

boot/src/main/kotlin/org/freekode/tp2intervals/app/workout/schedule/WorkoutScheduledJob.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ class WorkoutScheduledJob(
7373
}
7474

7575
@Scheduled(
76-
fixedRate = 60,
77-
timeUnit = TimeUnit.MINUTES
76+
fixedRateString = "\${app.scheduler.interval-hours}",
77+
timeUnit = TimeUnit.HOURS
7878
)
7979
fun job() {
8080
val requests = scheduleRequestRepository
@@ -113,4 +113,4 @@ class WorkoutScheduledJob(
113113
C2CTodayScheduledRequest::class.java
114114
)
115115
}
116-
}
116+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.freekode.tp2intervals.infrastructure.configuration
2+
3+
import jakarta.validation.constraints.Max
4+
import jakarta.validation.constraints.Min
5+
import org.springframework.boot.context.properties.ConfigurationProperties
6+
import org.springframework.validation.annotation.Validated
7+
8+
@Validated
9+
@ConfigurationProperties("app.scheduler")
10+
data class SchedulerProperties(
11+
@field:Min(1)
12+
@field:Max(24)
13+
val intervalHours: Long,
14+
)

boot/src/main/resources/application.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
app:
2+
scheduler:
3+
interval-hours: ${SCHEDULER_INTERVAL_HOURS:1}
24
training-peaks:
35
api-url: https://tpapi.trainingpeaks.com
46
intervals:

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ services:
66
restart: unless-stopped
77
environment:
88
JAVA_TOOL_OPTIONS: "-Duser.timezone=Europe/Lisbon"
9+
SCHEDULER_INTERVAL_HOURS: "${SCHEDULER_INTERVAL_HOURS:-1}"
910
ports:
1011
- "8098:8080"
1112
volumes:
12-
- ./data:/data
13+
- ./data:/data

ui/src/app/automation/automation.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ <h1>Automation</h1>
55

66
<p>
77
Configure recurring workout synchronizations. Scheduled syncs
8-
currently run every 60 minutes for the current day.
8+
run at the configured interval for the current day.
99
</p>
1010
</section>
1111

@@ -104,7 +104,7 @@ <h2>Scheduled syncs</h2>
104104
</mat-card-title>
105105

106106
<mat-card-subtitle>
107-
Every 60 minutes · Current day
107+
Configured interval · Current day
108108
</mat-card-subtitle>
109109
</mat-card-header>
110110

@@ -265,4 +265,4 @@ <h2>Sync history</h2>
265265
</div>
266266
</section>
267267

268-
</div>
268+
</div>

ui/src/infrastructure/notification.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export class NotificationService {
162162
}
163163

164164
scheduledSyncCreated(): void {
165-
this.success('Scheduled sync created. It will run every 60 minutes for today.');
165+
this.success('Scheduled sync created. It will run at the configured interval for today.');
166166
}
167167

168168
scheduledSyncDeleted(): void {

0 commit comments

Comments
 (0)