Skip to content

Commit

Permalink
feat: add create Activity
Browse files Browse the repository at this point in the history
It is yet incomplete, as it takes the data from a file in its resources. It would ideally use data received from frontend. But still, given a gpx file, we can parse it into an Activity.
  • Loading branch information
AngelMarquesGarcia committed Nov 5, 2024
1 parent 2e4e6c5 commit cd31650
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ build/

### VS Code ###
.vscode/
src/main/resources/static/track.gpx
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@
</dependency>
<!-- </editor-fold> -->

<dependency>
<groupId>io.jenetics</groupId>
<artifactId>jpx</artifactId>
<version>3.1.0</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package codeurjc_students.ATRA.controller;

import codeurjc_students.ATRA.model.Activity;
import codeurjc_students.ATRA.service.ActivityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;


@RestController
@RequestMapping("/api/activities")
public class ActivityController {

@Autowired
private ActivityService activityService;

public Activity getActivity(){
return null;
}

@PostMapping
public ResponseEntity<Activity> createActivity(Principal principal){
activityService.newActivity("target\\classes\\static\\track.gpx", principal.getName());
return ResponseEntity.ok().build();
}

public ResponseEntity<Activity> modifyActivity(){return null;}

public ResponseEntity<Activity> deleteActivity(){
return null;
}

}

20 changes: 20 additions & 0 deletions src/main/java/codeurjc_students/ATRA/model/Activity.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package codeurjc_students.ATRA.model;

import codeurjc_students.ATRA.model.auxiliary.DataPoint;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Data
@Setter
@Getter
Expand All @@ -17,4 +25,16 @@ public class Activity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String type;
private Instant startTime;

private Long user;

@ElementCollection
private List<DataPoint> dataPoints = new ArrayList<>();

public void addDataPoint(DataPoint dataPoint) {
dataPoints.add(dataPoint);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package codeurjc_students.ATRA.model.auxiliary;

import codeurjc_students.ATRA.service.MapToStringConverter;
import jakarta.persistence.*;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

/**
* Parallel to a GPX's trkpt, stores values for each metric at a specific time.
*/
@Data
@Setter
@Getter
@Embeddable
public class DataPoint {
private Instant _time;

private Double _lat;
private Double _long;
private Double _ele;

private int hr;
private int cad;

@Convert(converter = MapToStringConverter.class) //this means that, when serializing, it will be converted into a String
private Map<String, String> other = new HashMap<>();

public void put(String key, String value){
switch (key) {
case "lat" : _lat = Double.valueOf(value); break;
case "long": _long = Double.valueOf(value); break;
case "ele" : _ele = Double.valueOf(value); break;

case "time" : _time = Instant.parse(value); break;

case "hr" : hr = Integer.parseInt(value); break;
case "cad" : cad = Integer.parseInt(value); break;

default: other.put(key,value);
}
}

}
91 changes: 85 additions & 6 deletions src/main/java/codeurjc_students/ATRA/service/ActivityService.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,116 @@
package codeurjc_students.ATRA.service;

import codeurjc_students.ATRA.model.Activity;
import codeurjc_students.ATRA.model.User;
import codeurjc_students.ATRA.model.auxiliary.DataPoint;
import codeurjc_students.ATRA.repository.ActivityRepository;
import io.jenetics.jpx.GPX;
import io.jenetics.jpx.WayPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.io.IOException;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.List;
import java.util.Optional;

@Service
public class ActivityService {

@Autowired
private ActivityRepository repository;
private ActivityRepository activityRepository;

@Autowired
private UserService userService;


public Optional<Activity> findById(long id) {
return repository.findById(id);
return activityRepository.findById(id);
}

public boolean exists(long id) {
return repository.existsById(id);
return activityRepository.existsById(id);
}

public List<Activity> findAll() {
return repository.findAll();
return activityRepository.findAll();
}

public void save(Activity activity) {
repository.save(activity);
activityRepository.save(activity);
}

public void delete(long id) {
repository.deleteById(id);
activityRepository.deleteById(id);
}


public Activity newActivity(String pathString, String username){
final GPX gpx;
try {
gpx = GPX.read(Paths.get(pathString));
} catch (IOException e) {throw new RuntimeException(e);}
List<WayPoint> pts = gpx.getTracks().get(0).getSegments().get(0).getPoints();


Activity activity = new Activity();
//set user
Optional<User> userOpt = userService.findByUserName(username);
if (userOpt.isEmpty()) return null; //or throw exception caught above
activity.setUser(userOpt.get().getId());

//process the metadata
gpx.getMetadata().ifPresent(metadata -> activity.setStartTime(metadata.getTime().get()));
activity.setName(gpx.getTracks().get(0).getName().get());
activity.setType(gpx.getTracks().get(0).getType().get());
//process the waypoints
for (WayPoint pt: pts) {
//add each waypoint to activity
addWayPoint(activity, pt);
}
activityRepository.save(activity);
return activity;
}

private void addWayPoint(Activity activity, WayPoint pt) {
//processes the WayPoint and adds it to activity in ATRA format
DataPoint dataPoint = new DataPoint();
//handle lat, long, ele
double latitude = pt.getLatitude().doubleValue();
double longitude = pt.getLongitude().doubleValue();
double elevation = (pt.getElevation().isPresent() ? pt.getElevation().get().doubleValue() : 0.0);

dataPoint.put("lat", Double.toString(latitude));
dataPoint.put("long", Double.toString(longitude));
dataPoint.put("ele", Double.toString(elevation));

//hanlde time
Optional<Instant> timeOpt = pt.getTime();
timeOpt.ifPresent(instant -> dataPoint.put("time", instant.toString()));

//handle extensions
Optional<Document> extensions = pt.getExtensions();
if (extensions.isEmpty()) return;
Element element = extensions.get().getDocumentElement();

Node currentMetric = element.getFirstChild().getChildNodes().item(0);
while (currentMetric!=null) {
//extract the value
String metric = currentMetric.getNodeName();
String metricValue = currentMetric.getFirstChild().getNodeValue();

if (metric.startsWith("gpxtpx:")) metric = metric.substring(7);
else System.out.println("Found a metric that does not start with 'gpxtcx:'"); //ideally throw an exception or sth but for now this works

dataPoint.put(metric, metricValue);
currentMetric = currentMetric.getNextSibling();
}

activity.addDataPoint(dataPoint);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package codeurjc_students.ATRA.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

import java.util.Map;

@Converter
public class MapToStringConverter implements AttributeConverter<Map<String,String>, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Map<String, String> map) {
try {
return objectMapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
System.out.println("There was an error reading some JSON");
throw new RuntimeException(e);
}
}

@Override
public Map<String, String> convertToEntityAttribute(String s) {
try {
return objectMapper.readValue(s, new TypeReference<>() {});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

0 comments on commit cd31650

Please sign in to comment.