Get activity title from uploaded file, if it is present #7

Merged
kabupatix merged 4 commits from 8-use-activity-name-from-file into main 2026-04-13 10:12:38 +02:00
5 changed files with 56 additions and 15 deletions

View file

@ -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;

View file

@ -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.
@ -80,8 +82,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<Element> track = getFirstTrack(doc);
if (track.isPresent()) {
extractActivityType(track.get(), parsedData);
extractActivityTitle(track.get(), parsedData);
}
// Determine timezone from first GPS coordinate
determineTimezone(parsedData);
@ -111,6 +117,8 @@ public class GpxParser {
}
}
/**
* Extracts track points from GPX document.
*/
@ -245,21 +253,40 @@ public class GpxParser {
}
}
/**
* Extracts activity type from GPX metadata.
/*
* Returns the first <trk> element from the GPS XML
*/
private void extractActivityType(Document doc, ParsedActivityData parsedData) {
private Optional<Element> 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() > 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);
}
}

View file

@ -20,6 +20,9 @@ import java.util.List;
*/
@Data
public class ParsedActivityData {
static final int MAX_TITLE_LENGTH = 255;
private List<TrackPointData> trackPoints = new ArrayList<>();
private LocalDateTime startTime;
private LocalDateTime endTime;
@ -30,6 +33,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

View file

@ -112,6 +112,10 @@ class GpxParserIntegrationTest {
// Verify at least some basic data
assertNotNull(parsedData.getActivityType(), "Activity type should be determined");
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 <type>running</type>)");
assertTrue(parsedData.getTrackPoints().size() > 0, "Should have at least one track point");

View file

@ -4,7 +4,7 @@
<time>2022-07-03T19:47:51Z</time>
</metadata>
<trk>
<name>Einmal Frust loswerden</name>
<name>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</name>
<type>running</type>
<trkseg>
<trkpt lat="48.0140070" lon="7.8513840">