From d4427ebd85ba4b2bf919631fda69a5bb5add590c Mon Sep 17 00:00:00 2001 From: Niklas Deutschmann Date: Sun, 12 Apr 2026 12:58:21 +0200 Subject: [PATCH 1/4] Use activity title from GPX file when present --- .../fitpub/service/ActivityFileService.java | 14 ++++-- .../net/javahippie/fitpub/util/GpxParser.java | 45 ++++++++++++++----- .../fitpub/util/ParsedActivityData.java | 1 + 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/javahippie/fitpub/service/ActivityFileService.java b/src/main/java/net/javahippie/fitpub/service/ActivityFileService.java index dcd60f7..060d0f6 100644 --- a/src/main/java/net/javahippie/fitpub/service/ActivityFileService.java +++ b/src/main/java/net/javahippie/fitpub/service/ActivityFileService.java @@ -378,10 +378,16 @@ public class ActivityFileService { byte[] rawFile, ProcessingOptions options ) throws JsonProcessingException { - // Generate title if not provided - String activityTitle = title != null && !title.isBlank() - ? title - : ActivityFormatter.generateActivityTitle(parsedData.getStartTime(), parsedData.getActivityType()); + String activityTitle; + if (title != null && !title.isBlank()) { + activityTitle = title; + } else if (parsedData.getTitle() != null) { + // Try to use title from input file + activityTitle = parsedData.getTitle(); + } else { + // Generate title if not provided + activityTitle = ActivityFormatter.generateActivityTitle(parsedData.getStartTime(), parsedData.getActivityType()); + } // Default to PUBLIC if visibility not specified Activity.Visibility activityVisibility = visibility != null ? visibility : Activity.Visibility.PRIVATE; diff --git a/src/main/java/net/javahippie/fitpub/util/GpxParser.java b/src/main/java/net/javahippie/fitpub/util/GpxParser.java index 0900af8..a06dc51 100644 --- a/src/main/java/net/javahippie/fitpub/util/GpxParser.java +++ b/src/main/java/net/javahippie/fitpub/util/GpxParser.java @@ -80,8 +80,12 @@ public class GpxParser { // Calculate duration parsedData.setTotalDuration(Duration.between(firstPoint.getTimestamp(), lastPoint.getTimestamp())); - // Extract activity type from metadata - extractActivityType(doc, parsedData); + // Extract activity type and title from metadata + Optional track = getFirstTrack(doc); + if (track.isPresent()) { + extractActivityType(track.get(), parsedData); + extractActivityTitle(track.get(), parsedData); + } // Determine timezone from first GPS coordinate determineTimezone(parsedData); @@ -111,6 +115,8 @@ public class GpxParser { } } + + /** * Extracts track points from GPX document. */ @@ -245,21 +251,40 @@ public class GpxParser { } } - /** - * Extracts activity type from GPX metadata. + /* + * Returns the first element from the GPS XML */ - private void extractActivityType(Document doc, ParsedActivityData parsedData) { + private Optional getFirstTrack(Document doc) { NodeList tracks = doc.getElementsByTagName("trk"); if (tracks.getLength() == 0) { tracks = doc.getElementsByTagNameNS("*", "trk"); } - if (tracks.getLength() > 0) { - Element track = (Element) tracks.item(0); - String type = getElementText(track, "type"); - if (type != null) { - parsedData.setActivityType(mapGpxTypeToActivityType(type)); + return tracks.getLength() > 0 ? Optional.of((Element) tracks.item(0)) : Optional.empty(); + } + + /** + * Extracts activity type from GPX metadata. + */ + private void extractActivityType(Element track, ParsedActivityData parsedData) { + String type = getElementText(track, "type"); + if (type != null) { + parsedData.setActivityType(mapGpxTypeToActivityType(type)); + } + } + + /** + * Extracts activity title from GPX metadata. + */ + private void extractActivityTitle(Element track, ParsedActivityData parsedData) { + String title = getElementText(track, "name"); + if (title != null) { + String shortenedTitle = title; + if (title.length() > 255) { + log.debug("Activity title was shortened to 255 characters: {}", title); + shortenedTitle = title.substring(0, 255); } + parsedData.setTitle(shortenedTitle); } } diff --git a/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java b/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java index 12ff347..a64811a 100644 --- a/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java +++ b/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java @@ -30,6 +30,7 @@ public class ParsedActivityData { private BigDecimal elevationGain; private BigDecimal elevationLoss; private Activity.ActivityType activityType = Activity.ActivityType.OTHER; + private String title; private ActivityMetricsData metrics; private String sourceFormat; // "FIT" or "GPX" private Boolean indoor = false; // Indicates if this is an indoor activity -- 2.49.1 From a3e06d33474d139826cb8836e160f3b2d3adbd94 Mon Sep 17 00:00:00 2001 From: Niklas Deutschmann Date: Sun, 12 Apr 2026 13:02:27 +0200 Subject: [PATCH 2/4] Extend test --- .../net/javahippie/fitpub/util/GpxParserIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java b/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java index 55ab855..9752c29 100644 --- a/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java +++ b/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java @@ -112,6 +112,7 @@ class GpxParserIntegrationTest { // Verify at least some basic data assertNotNull(parsedData.getActivityType(), "Activity type should be determined"); + assertEquals("Einmal Frust loswerden", parsedData.getTitle()); assertEquals(Activity.ActivityType.RUN, parsedData.getActivityType(), "Activity type should be RUN (from GPX running)"); assertTrue(parsedData.getTrackPoints().size() > 0, "Should have at least one track point"); @@ -379,6 +380,7 @@ class GpxParserIntegrationTest { // Test converting to entity structures Activity.ActivityType activityType = parsedData.getActivityType(); assertNotNull(activityType, "Activity type should be extracted"); + assertEquals("Einmal Frust loswerden", parsedData.getTitle()); assertEquals(Activity.ActivityType.RUN, activityType, "Activity should be detected as RUN from GPX running"); -- 2.49.1 From d2d58483d902ea6b4d58e77b84471c83fd95ef5d Mon Sep 17 00:00:00 2001 From: Niklas Deutschmann Date: Sun, 12 Apr 2026 13:07:23 +0200 Subject: [PATCH 3/4] Test shortening of long names --- .../javahippie/fitpub/util/GpxParserIntegrationTest.java | 6 ++++-- src/test/resources/7410863774.gpx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java b/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java index 9752c29..7bad2cb 100644 --- a/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java +++ b/src/test/java/net/javahippie/fitpub/util/GpxParserIntegrationTest.java @@ -112,7 +112,10 @@ class GpxParserIntegrationTest { // Verify at least some basic data assertNotNull(parsedData.getActivityType(), "Activity type should be determined"); - assertEquals("Einmal Frust loswerden", parsedData.getTitle()); + String parsedTitle = parsedData.getTitle(); + assertEquals(255, parsedTitle.length()); + assertTrue(parsedTitle.startsWith("Einmal Frust loswerden")); + assertFalse(parsedTitle.contains("Shouldn't appear")); assertEquals(Activity.ActivityType.RUN, parsedData.getActivityType(), "Activity type should be RUN (from GPX running)"); assertTrue(parsedData.getTrackPoints().size() > 0, "Should have at least one track point"); @@ -380,7 +383,6 @@ class GpxParserIntegrationTest { // Test converting to entity structures Activity.ActivityType activityType = parsedData.getActivityType(); assertNotNull(activityType, "Activity type should be extracted"); - assertEquals("Einmal Frust loswerden", parsedData.getTitle()); assertEquals(Activity.ActivityType.RUN, activityType, "Activity should be detected as RUN from GPX running"); diff --git a/src/test/resources/7410863774.gpx b/src/test/resources/7410863774.gpx index 848716a..39647af 100644 --- a/src/test/resources/7410863774.gpx +++ b/src/test/resources/7410863774.gpx @@ -4,7 +4,7 @@ - Einmal Frust loswerden + Einmal Frust loswerden blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel blafasel Shouldn't appear running -- 2.49.1 From 3002427e38a2175c6e42995adf06d657ce31bcfa Mon Sep 17 00:00:00 2001 From: Niklas Deutschmann Date: Sun, 12 Apr 2026 13:19:46 +0200 Subject: [PATCH 4/4] Extract constant --- src/main/java/net/javahippie/fitpub/util/GpxParser.java | 8 +++++--- .../net/javahippie/fitpub/util/ParsedActivityData.java | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/javahippie/fitpub/util/GpxParser.java b/src/main/java/net/javahippie/fitpub/util/GpxParser.java index a06dc51..f66c4d6 100644 --- a/src/main/java/net/javahippie/fitpub/util/GpxParser.java +++ b/src/main/java/net/javahippie/fitpub/util/GpxParser.java @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static net.javahippie.fitpub.util.ParsedActivityData.MAX_TITLE_LENGTH; + /** * Parser for GPX (GPS Exchange Format) files. * Extracts GPS coordinates, activity metrics from track points. @@ -280,9 +282,9 @@ public class GpxParser { String title = getElementText(track, "name"); if (title != null) { String shortenedTitle = title; - if (title.length() > 255) { - log.debug("Activity title was shortened to 255 characters: {}", title); - shortenedTitle = title.substring(0, 255); + if (title.length() > MAX_TITLE_LENGTH) { + log.debug("Activity title was shortened to {} characters: {}", MAX_TITLE_LENGTH, title); + shortenedTitle = title.substring(0, MAX_TITLE_LENGTH); } parsedData.setTitle(shortenedTitle); } diff --git a/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java b/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java index a64811a..d345f91 100644 --- a/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java +++ b/src/main/java/net/javahippie/fitpub/util/ParsedActivityData.java @@ -20,6 +20,9 @@ import java.util.List; */ @Data public class ParsedActivityData { + + static final int MAX_TITLE_LENGTH = 255; + private List trackPoints = new ArrayList<>(); private LocalDateTime startTime; private LocalDateTime endTime; -- 2.49.1