Skip to content

Commit 96a8d0c

Browse files
committed
Truncate fields while exporting data in CSV format to Hastus.
Resolves HSLdevcom/jore4#1568
1 parent af52769 commit 96a8d0c

4 files changed

Lines changed: 173 additions & 43 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<groupId>fi.hsl.jore4</groupId>
1414
<artifactId>hastus</artifactId>
1515
<packaging>jar</packaging>
16-
<version>1.2.1</version>
16+
<version>1.2.2</version>
1717
<name>Jore4 Hastus Server</name>
1818
<description>Jore4 hastus import/export server</description>
1919

src/main/kotlin/fi/hsl/jore4/hastus/Constants.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,10 @@ object Constants {
1717

1818
// vehicle schedule frame (timetables) priorities
1919
const val VEHICLE_SCHEDULE_FRAME_PRIORITY_STAGING = 40
20+
21+
// Hastus field length limits
22+
const val MAX_LENGTH_HASTUS_ROUTE_DESCRIPTION = 50
23+
const val MAX_LENGTH_HASTUS_ROUTE_VARIANT_DESCRIPTION = 60
24+
const val MAX_LENGTH_HASTUS_STOP_NAME = 100
25+
const val MAX_LENGTH_HASTUS_STOP_STREET_NAME = 50
2026
}

src/main/kotlin/fi/hsl/jore4/hastus/service/exporting/ConversionsToHastus.kt

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package fi.hsl.jore4.hastus.service.exporting
22

3+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_ROUTE_DESCRIPTION
4+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_ROUTE_VARIANT_DESCRIPTION
5+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_STOP_NAME
6+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_STOP_STREET_NAME
37
import fi.hsl.jore4.hastus.data.format.JoreRouteDirection
48
import fi.hsl.jore4.hastus.data.format.NumberWithAccuracy
59
import fi.hsl.jore4.hastus.data.hastus.IHastusData
@@ -18,49 +22,66 @@ import fi.hsl.jore4.hastus.data.jore.JoreTimingPlace
1822

1923
object ConversionsToHastus {
2024

21-
fun convertJoreLinesToHastus(lines: List<JoreLine>): List<IHastusData> {
22-
return lines.flatMap { joreLine ->
23-
val hastusRoute = Route(
24-
identifier = joreLine.label,
25-
description = joreLine.name,
26-
serviceType = 0,
27-
direction = 0,
28-
serviceMode = joreLine.vehicleMode
29-
)
25+
fun convertJoreLinesToHastus(lines: List<JoreLine>): List<IHastusData> = lines.flatMap { joreLine ->
26+
listOf(
27+
convertJoreLineToHastus(joreLine)
28+
).plus(
29+
convertJoreRoutesToHastusRouteVariants(joreLine.routes, joreLine.label)
30+
)
31+
}
3032

31-
listOf(hastusRoute) + convertJoreRoutesToHastusRouteVariants(joreLine.routes, joreLine.label)
32-
}
33+
internal fun convertJoreLineToHastus(joreLine: JoreLine): Route {
34+
// The cut is done due to length limit in Hastus.
35+
val hastusRouteDescription: String = joreLine.name.take(MAX_LENGTH_HASTUS_ROUTE_DESCRIPTION)
36+
37+
return Route(
38+
identifier = joreLine.label,
39+
description = hastusRouteDescription,
40+
serviceType = 0,
41+
direction = 0,
42+
serviceMode = joreLine.vehicleMode
43+
)
3344
}
3445

3546
private fun convertJoreRoutesToHastusRouteVariants(
3647
joreRoutes: List<JoreRoute>,
3748
joreLineLabel: String
3849
): List<IHastusData> {
3950
return joreRoutes.flatMap { joreRoute ->
40-
val hastusRouteVariantId: String =
41-
getHastusRouteVariantId(joreLineLabel, joreRoute.label, joreRoute.variant, joreRoute.direction)
42-
val hastusRouteIdAndVariantId: String = joreLineLabel + hastusRouteVariantId
43-
val hastusRouteVariantDirection: Int = getRouteDirectionAsNumberOrThrow(joreRoute.direction) - 1
44-
45-
val hastusRouteVariant = RouteVariant(
46-
identifier = hastusRouteVariantId,
47-
description = joreRoute.name,
48-
direction = hastusRouteVariantDirection,
49-
reversible = joreRoute.reversible,
50-
routeIdAndVariantId = hastusRouteIdAndVariantId,
51-
routeId = joreLineLabel
52-
)
51+
val hastusRouteVariant = convertJoreRouteToHastusRouteVariant(joreRoute, joreLineLabel)
5352

5453
val hastusRouteVariantPoints: List<RouteVariantPoint> =
5554
convertJoreStopPointsInJourneyPatternToHastusRouteVariantPoints(
5655
joreRoute.stopPointsInJourneyPattern,
57-
hastusRouteIdAndVariantId
56+
hastusRouteVariant.routeIdAndVariantId
5857
)
5958

6059
listOf(hastusRouteVariant) + hastusRouteVariantPoints
6160
}
6261
}
6362

63+
internal fun convertJoreRouteToHastusRouteVariant(
64+
joreRoute: JoreRoute,
65+
joreLineLabel: String
66+
): RouteVariant {
67+
val hastusRouteVariantId: String =
68+
getHastusRouteVariantId(joreLineLabel, joreRoute.label, joreRoute.variant, joreRoute.direction)
69+
val hastusRouteIdAndVariantId: String = joreLineLabel + hastusRouteVariantId
70+
val hastusRouteVariantDirection: Int = getRouteDirectionAsNumberOrThrow(joreRoute.direction) - 1
71+
72+
// The cut is done due to length limit in Hastus.
73+
val hastusRouteVariantDescription: String = joreRoute.name.take(MAX_LENGTH_HASTUS_ROUTE_VARIANT_DESCRIPTION)
74+
75+
return RouteVariant(
76+
identifier = hastusRouteVariantId,
77+
description = hastusRouteVariantDescription,
78+
direction = hastusRouteVariantDirection,
79+
reversible = joreRoute.reversible,
80+
routeIdAndVariantId = hastusRouteIdAndVariantId,
81+
routeId = joreLineLabel
82+
)
83+
}
84+
6485
internal fun getHastusRouteVariantId(
6586
joreLineLabel: String,
6687
joreRouteLabel: String,
@@ -121,23 +142,30 @@ object ConversionsToHastus {
121142
}
122143
}
123144

124-
fun convertJoreStopPointsToHastus(
125-
joreStopPoints: List<JoreScheduledStop>
126-
): List<Stop> {
127-
return joreStopPoints.map {
128-
Stop(
129-
identifier = it.label,
130-
platform = it.platform,
131-
descriptionFinnish = it.nameFinnish,
132-
descriptionSwedish = it.nameSwedish,
133-
streetFinnish = it.streetNameFinnish,
134-
streetSwedish = it.streetNameSwedish,
135-
place = it.timingPlaceShortName,
136-
gpsX = NumberWithAccuracy(it.location.x, 2, 6),
137-
gpsY = NumberWithAccuracy(it.location.y, 2, 6),
138-
shortIdentifier = it.label
139-
)
140-
}
145+
fun convertJoreStopPointsToHastus(joreStopPoints: List<JoreScheduledStop>): List<Stop> =
146+
joreStopPoints.map(this::convertJoreStopPointToHastus)
147+
148+
internal fun convertJoreStopPointToHastus(joreStopPoint: JoreScheduledStop): Stop {
149+
// The cuts below are done due to length limits in Hastus.
150+
151+
val stopNameFi = joreStopPoint.nameFinnish.take(MAX_LENGTH_HASTUS_STOP_NAME)
152+
val stopNameSv = joreStopPoint.nameSwedish.take(MAX_LENGTH_HASTUS_STOP_NAME)
153+
154+
val stopStreetNameFi = joreStopPoint.streetNameFinnish.take(MAX_LENGTH_HASTUS_STOP_STREET_NAME)
155+
val stopStreetNameSv = joreStopPoint.streetNameSwedish.take(MAX_LENGTH_HASTUS_STOP_STREET_NAME)
156+
157+
return Stop(
158+
identifier = joreStopPoint.label,
159+
platform = joreStopPoint.platform,
160+
descriptionFinnish = stopNameFi,
161+
descriptionSwedish = stopNameSv,
162+
streetFinnish = stopStreetNameFi,
163+
streetSwedish = stopStreetNameSv,
164+
place = joreStopPoint.timingPlaceShortName,
165+
gpsX = NumberWithAccuracy(joreStopPoint.location.x, 2, 6),
166+
gpsY = NumberWithAccuracy(joreStopPoint.location.y, 2, 6),
167+
shortIdentifier = joreStopPoint.label
168+
)
141169
}
142170

143171
fun convertJoreTimingPlacesToHastus(

src/test/kotlin/fi/hsl/jore4/hastus/service/exporting/ConversionsToHastusTest.kt

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
package fi.hsl.jore4.hastus.service.exporting
22

3+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_ROUTE_DESCRIPTION
4+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_ROUTE_VARIANT_DESCRIPTION
5+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_STOP_NAME
6+
import fi.hsl.jore4.hastus.Constants.MAX_LENGTH_HASTUS_STOP_STREET_NAME
7+
import fi.hsl.jore4.hastus.data.format.Coordinate
38
import fi.hsl.jore4.hastus.data.format.JoreRouteDirection
9+
import fi.hsl.jore4.hastus.data.hastus.Route
10+
import fi.hsl.jore4.hastus.data.hastus.RouteVariant
11+
import fi.hsl.jore4.hastus.data.hastus.Stop
12+
import fi.hsl.jore4.hastus.data.jore.JoreLine
13+
import fi.hsl.jore4.hastus.data.jore.JoreRoute
14+
import fi.hsl.jore4.hastus.data.jore.JoreScheduledStop
415
import fi.hsl.jore4.hastus.service.exporting.ConversionsToHastus.getHastusRouteVariantId
516
import org.junit.jupiter.api.DisplayName
617
import org.junit.jupiter.api.Nested
718
import org.junit.jupiter.api.Test
19+
import java.time.LocalDate
20+
import java.util.UUID
821
import kotlin.test.assertEquals
922

1023
@DisplayName("Test type conversions from Jore to Hastus")
@@ -50,4 +63,87 @@ class ConversionsToHastusTest {
5063
assertEquals("BK52", getHastusRouteVariantId("123", "123BK", "5", JoreRouteDirection.INBOUND))
5164
}
5265
}
66+
67+
@DisplayName("Test method: convertJoreLineToHastus")
68+
@Nested
69+
inner class TestConvertJoreLineToHastus {
70+
71+
@Test
72+
fun `test truncation of length-limited fields`() {
73+
val name = "1234567890".repeat(MAX_LENGTH_HASTUS_ROUTE_DESCRIPTION / 10 * 20)
74+
75+
val hastusRoute: Route = ConversionsToHastus.convertJoreLineToHastus(
76+
JoreLine(
77+
label = "123",
78+
name = name,
79+
typeOfLine = "stopping_bus_service",
80+
vehicleMode = 0,
81+
routes = emptyList()
82+
)
83+
)
84+
85+
assertEquals("12345678901234567890123456789012345678901234567890", hastusRoute.description)
86+
}
87+
}
88+
89+
@DisplayName("Test method: convertJoreRouteToHastusRouteVariant")
90+
@Nested
91+
inner class TestConvertJoreRouteToHastusRouteVariant {
92+
93+
@Test
94+
fun `test truncation of length-limited fields`() {
95+
val name = "1234567890".repeat(MAX_LENGTH_HASTUS_ROUTE_VARIANT_DESCRIPTION / 10 * 20)
96+
97+
val hastusRouteVariant: RouteVariant = ConversionsToHastus.convertJoreRouteToHastusRouteVariant(
98+
JoreRoute(
99+
label = "123B",
100+
variant = null,
101+
name = name,
102+
direction = JoreRouteDirection.OUTBOUND,
103+
reversible = false,
104+
validityStart = LocalDate.of(2023, 1, 1),
105+
validityEnd = LocalDate.of(2050, 12, 31),
106+
typeOfLine = "stopping_bus_service",
107+
journeyPatternId = UUID.randomUUID(),
108+
stopPointsInJourneyPattern = emptyList()
109+
),
110+
"123"
111+
)
112+
113+
assertEquals("123456789012345678901234567890123456789012345678901234567890", hastusRouteVariant.description)
114+
}
115+
}
116+
117+
@DisplayName("Test method: convertJoreStopPointToHastus")
118+
@Nested
119+
inner class TestConvertJoreStopPointToHastus {
120+
121+
@Test
122+
fun `test truncation of length-limited fields`() {
123+
val hastusStop: Stop = ConversionsToHastus.convertJoreStopPointToHastus(
124+
JoreScheduledStop(
125+
label = "H1234",
126+
platform = "00",
127+
nameFinnish = "1234567890".repeat(MAX_LENGTH_HASTUS_STOP_NAME / 10 * 20),
128+
nameSwedish = "9876543210".repeat(MAX_LENGTH_HASTUS_STOP_NAME / 10 * 20),
129+
streetNameFinnish = "ABCDEFGHIJ".repeat(MAX_LENGTH_HASTUS_STOP_STREET_NAME / 10 * 20),
130+
streetNameSwedish = "JIHGFEDCBA".repeat(MAX_LENGTH_HASTUS_STOP_STREET_NAME / 10 * 20),
131+
timingPlaceShortName = "1AACKT",
132+
location = Coordinate(24.928327, 60.163918)
133+
)
134+
)
135+
136+
assertEquals(
137+
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
138+
hastusStop.descriptionFinnish
139+
)
140+
assertEquals(
141+
"9876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210",
142+
hastusStop.descriptionSwedish
143+
)
144+
145+
assertEquals("ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ", hastusStop.streetFinnish)
146+
assertEquals("JIHGFEDCBAJIHGFEDCBAJIHGFEDCBAJIHGFEDCBAJIHGFEDCBA", hastusStop.streetSwedish)
147+
}
148+
}
53149
}

0 commit comments

Comments
 (0)