Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2 Calculation of the score #4

Merged
merged 5 commits into from
Nov 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
compile 'org.springframework.boot:spring-boot-starter-data-rest'
compile 'org.springframework.boot:spring-boot-starter-jdbc'
compile 'org.springframework.data:spring-data-rest-hal-browser'
compile 'com.fasterxml.jackson.core:jackson-core'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand All @@ -46,4 +47,6 @@ wrapper {

test {
systemProperty 'spring.profiles.active', 'local'
}
}

compileJava.dependsOn(processResources)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.bjs.bjsapi.config;

import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "api.calculation")
public class CalculationInformationConfig {

private String calculationInformationFile;

public String getCalculationInformationFile() {
return calculationInformationFile;
}

public Path getCalculationInformationFilePath() {
return Paths.get(calculationInformationFile);
}

public void setCalculationInformationFile(String calculationInformationFile) {
this.calculationInformationFile = calculationInformationFile;
}

}
34 changes: 34 additions & 0 deletions api/src/main/java/com/bjs/bjsapi/config/RepositoryConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.bjs.bjsapi.config;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceProcessor;

import com.bjs.bjsapi.controllers.StudentScoreController;
import com.bjs.bjsapi.database.model.Student;

@Configuration
public class RepositoryConfig {

@SuppressWarnings("Convert2Lambda")
@Bean
public ResourceProcessor<Resource<Student>> personProcessor() {

return new ResourceProcessor<Resource<Student>>() {
@Override
public Resource<Student> process(Resource<Student> resource) {
Student content = resource.getContent();
if (content != null) {
Long id = content.getId();
resource.add(linkTo(StudentScoreController.class, id).slash("score").withRel("score"));
}

return resource;
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.bjs.bjsapi.controllers;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.bjs.bjsapi.database.model.SportResult;
import com.bjs.bjsapi.database.model.Student;
import com.bjs.bjsapi.database.repository.SportResultRepository;
import com.bjs.bjsapi.database.repository.StudentRepository;
import com.bjs.bjsapi.helper.CalculationInformationService;

@RestController
@RequestMapping("/api/v1/students/{id}")
public class StudentScoreController {

private final StudentRepository studentRepository;
private final SportResultRepository sportResultRepository;
private final CalculationInformationService calculationInformationService;

public StudentScoreController(StudentRepository studentRepository, SportResultRepository sportResultRepository, CalculationInformationService calculationInformationService) {
this.studentRepository = studentRepository;
this.sportResultRepository = sportResultRepository;
this.calculationInformationService = calculationInformationService;
}

@GetMapping("/score")
public ResponseEntity<?> returnScore(@PathVariable("id") Long id) {
return studentRepository.findById(id)
.map(this::calculateScore)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

Integer calculateScore(Student student) {
return sportResultRepository.findByStudent(student)
.stream()
.mapToInt(sportResult -> calculatePoints(sportResult, student.getFemale()))
.sum();
}

private int calculatePoints(SportResult sportResult, Boolean female) {
if (sportResult.getDiscipline().isRUN()) {
return calculateRunningPoints(sportResult, female);
} else {
return calculateNotRunningPoints(sportResult, female);
}
}

private int calculateNotRunningPoints(SportResult sportResult, Boolean female) {
// P = ( √M - a ) / c

final double measurement = sportResult.getResult();

final double a = calculationInformationService.getAValue(female, sportResult.getDiscipline());
final double c = calculationInformationService.getCValue(female, sportResult.getDiscipline());

final double points = (Math.sqrt(measurement) - a) / c;
return (int) Math.floor(points);
}

private int calculateRunningPoints(SportResult sportResult, Boolean female) {
// P = ( D : (M + Z) ) - a) / c

final double distance = sportResult.getDiscipline().getDistance();
final double measurement = sportResult.getResult();

final double extra = calculateExtra(sportResult.getDiscipline().getDistance());

final double a = calculationInformationService.getAValue(female, sportResult.getDiscipline());
final double c = calculationInformationService.getCValue(female, sportResult.getDiscipline());

final double points = ((distance / (measurement + extra)) - a) / c;
return (int) Math.floor(points);
}

private double calculateExtra(int distance) {
double extra = 0.0;
if (distance <= 300) {
extra = 0.24;
} else if (distance <= 400) {
extra = 0.14;
}

return extra;
}

}
10 changes: 6 additions & 4 deletions api/src/main/java/com/bjs/bjsapi/database/model/Student.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;

import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.hateoas.Identifiable;

@Entity
@Table(name = "students")
public class Student {
public class Student implements Identifiable<Long> {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Expand All @@ -35,7 +34,6 @@ public class Student {
private Date birthDay;

@ManyToOne
@RestResource(path = "class", rel = "class")
@JoinColumn
private Class schoolClass;

Expand Down Expand Up @@ -82,4 +80,8 @@ public void setSchoolClass(Class schoolClass) {
this.schoolClass = schoolClass;
}

public Long getId() {
return id;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,37 @@
*/
public enum DisciplineType {

RUN_50,
RUN_75,
RUN_100,
RUN_800,
RUN_2000,
RUN_3000,

HIGH_JUMP,
LONG_JUMP,
SHOT_PUT,
SLING_BALL,
BALL_THROWING_80,
BALL_THROWING_200;
RUN_50(true, 50),
RUN_75(true, 75),
RUN_100(true, 100),
RUN_800(true, 800),
RUN_2000(true, 2000),
RUN_3000(true, 3000),

HIGH_JUMP(false),
LONG_JUMP(false),
SHOT_PUT(false),
SLING_BALL(false),
BALL_THROWING_80(false),
BALL_THROWING_200(false);

private final boolean isRun;
private int distance;

public int getDistance() {
aykborstelmann marked this conversation as resolved.
Show resolved Hide resolved
return distance;
}

DisciplineType(boolean isRun, int distance) {
this.isRun = isRun;
this.distance = distance;
}

DisciplineType(boolean isRun) {
this.isRun = isRun;
}

public boolean isRUN() {
return isRun;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.bjs.bjsapi.helper;

import java.io.IOException;
import java.nio.file.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.bjs.bjsapi.config.CalculationInformationConfig;
import com.bjs.bjsapi.database.model.enums.DisciplineType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Service
public class CalculationInformationService {

private static final Logger log = LoggerFactory.getLogger(CalculationInformationService.class);

private final CalculationInformationConfig calculationInformationConfig;

public CalculationInformationService(CalculationInformationConfig calculationInformationConfig) {
this.calculationInformationConfig = calculationInformationConfig;
}

public double getAValue(boolean female, DisciplineType discipline) {
return getValue(female, true, discipline);
}

public double getCValue(boolean female, DisciplineType discipline) {
return getValue(female, false, discipline);
}

double getValue(boolean female, boolean a, DisciplineType discipline) {
Path jsonFile = calculationInformationConfig.getCalculationInformationFilePath();
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode root = objectMapper.readTree(jsonFile.toFile());

JsonNode firstLayer = giveFirstLayer(root, female);
JsonNode secondLayer = giveSecondLayer(firstLayer, a);
JsonNode thirdLayer = giveThirdLayer(secondLayer, discipline);

return thirdLayer.asDouble();
} catch (IOException e) {
throw new IllegalArgumentException(String.format("Could not parse file \"%s\"", jsonFile), e);
}
}

private JsonNode giveThirdLayer(JsonNode secondLayer, DisciplineType discipline) {
return throwIfNotAccessible(secondLayer, discipline.toString());
}

private JsonNode giveFirstLayer(JsonNode root, boolean female) {
JsonNode firstLayer;
if (female) {
firstLayer = throwIfNotAccessible(root, "female");
} else {
firstLayer = throwIfNotAccessible(root, "male");
}
return firstLayer;
}

private JsonNode giveSecondLayer(JsonNode maleFemaleInformation, boolean a) {
JsonNode secondLayer;
if (a) {
secondLayer = throwIfNotAccessible(maleFemaleInformation, "a");
} else {
secondLayer = throwIfNotAccessible(maleFemaleInformation, "c");
}

return secondLayer;
}

private JsonNode throwIfNotAccessible(JsonNode fromJsonNode, String keyword) {
JsonNode returnJsonNode;
if (fromJsonNode.hasNonNull(keyword)) {
returnJsonNode = fromJsonNode.get(keyword);
} else {
throw new IllegalArgumentException(String.format("The JSON file has a wrong format. Could not find property \"%s\"", keyword));
}
return returnJsonNode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"properties": [
{
"name": "api.calculation.calculationInformationFile",
"type": "java.lang.String",
"description": "Path to json file which contains the variables used to calculate the score.",
"sourceType": "com.bjs.bjsapi.config.CalculationInformationConfig",
"defaultValue": "calculation_information.json"
}
]
}
Loading