Skip to content

Commit 3db2d92

Browse files
committed
code quality improvements
1 parent 7b4de16 commit 3db2d92

4 files changed

Lines changed: 123 additions & 77 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package de.tum.cit.aet.analysis.dto.cqi;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
5+
/**
6+
* Response DTO exposing CQI weight configuration for a specific exercise.
7+
*
8+
* @param effortBalance weight for effort balance (0-1)
9+
* @param locBalance weight for lines-of-code balance (0-1)
10+
* @param temporalSpread weight for temporal spread (0-1)
11+
* @param ownershipSpread weight for ownership spread (0-1)
12+
* @param isDefault {@code true} when the weights are application defaults
13+
*/
14+
@JsonIgnoreProperties(ignoreUnknown = true)
15+
public record CqiWeightsDTO(
16+
double effortBalance,
17+
double locBalance,
18+
double temporalSpread,
19+
double ownershipSpread,
20+
boolean isDefault
21+
) {}

src/main/java/de/tum/cit/aet/analysis/service/cqi/CqiWeightService.java

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package de.tum.cit.aet.analysis.service.cqi;
22

3+
import de.tum.cit.aet.analysis.domain.CqiWeightConfiguration;
4+
import de.tum.cit.aet.analysis.dto.cqi.CqiWeightsDTO;
35
import de.tum.cit.aet.analysis.repository.CqiWeightConfigurationRepository;
46
import lombok.RequiredArgsConstructor;
57
import org.springframework.stereotype.Service;
8+
import org.springframework.transaction.annotation.Transactional;
69

10+
/**
11+
* Service for managing per-exercise CQI weight configurations.
12+
*/
713
@Service
814
@RequiredArgsConstructor
915
public class CqiWeightService {
@@ -12,10 +18,10 @@ public class CqiWeightService {
1218
private final CQIConfig cqiConfig;
1319

1420
/**
15-
* Resolve weights for an exercise. Checks DB first, falls back to application.yml defaults.
21+
* Resolves weights for an exercise. Checks the database first, falls back to application defaults.
1622
*
17-
* @param exerciseId the exercise ID to resolve weights for, or null for defaults
18-
* @return the resolved weights configuration
23+
* @param exerciseId the exercise ID, or {@code null} for defaults
24+
* @return the resolved weights
1925
*/
2026
public CQIConfig.Weights getWeightsForExercise(Long exerciseId) {
2127
if (exerciseId == null) {
@@ -32,4 +38,69 @@ public CQIConfig.Weights getWeightsForExercise(Long exerciseId) {
3238
})
3339
.orElse(cqiConfig.getWeights());
3440
}
41+
42+
/**
43+
* Returns the CQI weights for an exercise, falling back to defaults if none are configured.
44+
*
45+
* @param exerciseId the exercise ID
46+
* @return weights DTO with {@code isDefault} indicating whether defaults are used
47+
*/
48+
public CqiWeightsDTO getWeights(Long exerciseId) {
49+
return weightConfigRepository.findByExerciseId(exerciseId)
50+
.map(config -> new CqiWeightsDTO(
51+
config.getEffortWeight(), config.getLocWeight(),
52+
config.getTemporalWeight(), config.getOwnershipWeight(), false))
53+
.orElseGet(() -> {
54+
CQIConfig.Weights w = cqiConfig.getWeights();
55+
return new CqiWeightsDTO(w.getEffort(), w.getLoc(), w.getTemporal(), w.getOwnership(), true);
56+
});
57+
}
58+
59+
/**
60+
* Saves custom CQI weights for an exercise.
61+
*
62+
* @param exerciseId the exercise ID
63+
* @param request the weights to save (must be non-negative and sum to 1.0)
64+
* @return the saved weights DTO
65+
* @throws IllegalArgumentException if the weights are invalid
66+
*/
67+
@Transactional
68+
public CqiWeightsDTO saveWeights(Long exerciseId, CqiWeightsDTO request) {
69+
if (request.effortBalance() < 0 || request.locBalance() < 0
70+
|| request.temporalSpread() < 0 || request.ownershipSpread() < 0) {
71+
throw new IllegalArgumentException("All weights must be non-negative");
72+
}
73+
double sum = request.effortBalance() + request.locBalance()
74+
+ request.temporalSpread() + request.ownershipSpread();
75+
if (Math.abs(sum - 1.0) >= 0.001) {
76+
throw new IllegalArgumentException("Weights must sum to 100% (got " + Math.round(sum * 100) + "%)");
77+
}
78+
79+
CqiWeightConfiguration config = weightConfigRepository.findByExerciseId(exerciseId)
80+
.orElseGet(() -> new CqiWeightConfiguration(exerciseId,
81+
request.effortBalance(), request.locBalance(),
82+
request.temporalSpread(), request.ownershipSpread()));
83+
config.setEffortWeight(request.effortBalance());
84+
config.setLocWeight(request.locBalance());
85+
config.setTemporalWeight(request.temporalSpread());
86+
config.setOwnershipWeight(request.ownershipSpread());
87+
weightConfigRepository.save(config);
88+
89+
return new CqiWeightsDTO(
90+
config.getEffortWeight(), config.getLocWeight(),
91+
config.getTemporalWeight(), config.getOwnershipWeight(), false);
92+
}
93+
94+
/**
95+
* Resets CQI weights for an exercise back to application defaults.
96+
*
97+
* @param exerciseId the exercise ID
98+
* @return the default weights DTO
99+
*/
100+
@Transactional
101+
public CqiWeightsDTO resetWeights(Long exerciseId) {
102+
weightConfigRepository.deleteByExerciseId(exerciseId);
103+
CQIConfig.Weights w = cqiConfig.getWeights();
104+
return new CqiWeightsDTO(w.getEffort(), w.getLoc(), w.getTemporal(), w.getOwnership(), true);
105+
}
35106
}
Lines changed: 23 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,61 @@
11
package de.tum.cit.aet.analysis.web;
22

3-
import de.tum.cit.aet.analysis.domain.CqiWeightConfiguration;
4-
import de.tum.cit.aet.analysis.repository.CqiWeightConfigurationRepository;
5-
import de.tum.cit.aet.analysis.service.cqi.CQIConfig;
3+
import de.tum.cit.aet.analysis.dto.cqi.CqiWeightsDTO;
4+
import de.tum.cit.aet.analysis.service.cqi.CqiWeightService;
65
import lombok.RequiredArgsConstructor;
76
import lombok.extern.slf4j.Slf4j;
87
import org.springframework.http.ResponseEntity;
9-
import org.springframework.transaction.annotation.Transactional;
108
import org.springframework.web.bind.annotation.*;
119

10+
/**
11+
* REST controller for per-exercise CQI weight configuration.
12+
*/
1213
@RestController
1314
@RequestMapping("/api/exercises/{exerciseId}/cqi-weights")
1415
@Slf4j
1516
@RequiredArgsConstructor
1617
public class CqiWeightResource {
1718

18-
private final CqiWeightConfigurationRepository weightConfigRepository;
19-
private final CQIConfig cqiConfig;
20-
21-
public record CqiWeightsDTO(
22-
double effortBalance,
23-
double locBalance,
24-
double temporalSpread,
25-
double ownershipSpread,
26-
Boolean isDefault
27-
) {}
19+
private final CqiWeightService cqiWeightService;
2820

2921
/**
30-
* Get the CQI weights for an exercise, falling back to defaults if none are configured.
22+
* Returns the CQI weights for an exercise, falling back to defaults if none are configured.
3123
*
3224
* @param exerciseId the exercise ID
33-
* @return the current weights configuration
25+
* @return the current weights
3426
*/
3527
@GetMapping
3628
public ResponseEntity<CqiWeightsDTO> getWeights(@PathVariable Long exerciseId) {
37-
return weightConfigRepository.findByExerciseId(exerciseId)
38-
.map(config -> ResponseEntity.ok(new CqiWeightsDTO(
39-
config.getEffortWeight(), config.getLocWeight(),
40-
config.getTemporalWeight(), config.getOwnershipWeight(), false)))
41-
.orElseGet(() -> {
42-
CQIConfig.Weights w = cqiConfig.getWeights();
43-
return ResponseEntity.ok(new CqiWeightsDTO(
44-
w.getEffort(), w.getLoc(), w.getTemporal(), w.getOwnership(), true));
45-
});
29+
log.info("GET cqi-weights for exerciseId={}", exerciseId);
30+
return ResponseEntity.ok(cqiWeightService.getWeights(exerciseId));
4631
}
4732

4833
/**
49-
* Save custom CQI weights for an exercise. Weights must sum to 1.0 and be non-negative.
34+
* Saves custom CQI weights for an exercise.
5035
*
5136
* @param exerciseId the exercise ID
52-
* @param request the weights to save
53-
* @return the saved weights configuration
37+
* @param request the weights to save
38+
* @return the saved weights
5439
*/
5540
@PutMapping
56-
@Transactional
57-
public ResponseEntity<?> saveWeights(
58-
@PathVariable Long exerciseId,
59-
@RequestBody CqiWeightsDTO request) {
60-
61-
if (request.effortBalance() < 0 || request.locBalance() < 0
62-
|| request.temporalSpread() < 0 || request.ownershipSpread() < 0) {
63-
return ResponseEntity.badRequest().body("All weights must be non-negative");
41+
public ResponseEntity<?> saveWeights(@PathVariable Long exerciseId, @RequestBody CqiWeightsDTO request) {
42+
log.info("PUT cqi-weights for exerciseId={}", exerciseId);
43+
try {
44+
return ResponseEntity.ok(cqiWeightService.saveWeights(exerciseId, request));
45+
} catch (IllegalArgumentException e) {
46+
return ResponseEntity.badRequest().body(e.getMessage());
6447
}
65-
double sum = request.effortBalance() + request.locBalance()
66-
+ request.temporalSpread() + request.ownershipSpread();
67-
if (Math.abs(sum - 1.0) >= 0.001) {
68-
return ResponseEntity.badRequest().body("Weights must sum to 100% (got " + Math.round(sum * 100) + "%)");
69-
}
70-
71-
CqiWeightConfiguration config = weightConfigRepository.findByExerciseId(exerciseId)
72-
.orElseGet(() -> new CqiWeightConfiguration(exerciseId,
73-
request.effortBalance(), request.locBalance(),
74-
request.temporalSpread(), request.ownershipSpread()));
75-
config.setEffortWeight(request.effortBalance());
76-
config.setLocWeight(request.locBalance());
77-
config.setTemporalWeight(request.temporalSpread());
78-
config.setOwnershipWeight(request.ownershipSpread());
79-
weightConfigRepository.save(config);
80-
81-
log.info("Saved CQI weights for exercise {}: effort={}, loc={}, temporal={}, ownership={}",
82-
exerciseId, config.getEffortWeight(), config.getLocWeight(),
83-
config.getTemporalWeight(), config.getOwnershipWeight());
84-
85-
return ResponseEntity.ok(new CqiWeightsDTO(
86-
config.getEffortWeight(), config.getLocWeight(),
87-
config.getTemporalWeight(), config.getOwnershipWeight(), false));
8848
}
8949

9050
/**
91-
* Reset CQI weights for an exercise back to the application defaults.
51+
* Resets CQI weights for an exercise back to application defaults.
9252
*
9353
* @param exerciseId the exercise ID
94-
* @return the default weights configuration
54+
* @return the default weights
9555
*/
9656
@DeleteMapping
97-
@Transactional
9857
public ResponseEntity<CqiWeightsDTO> resetWeights(@PathVariable Long exerciseId) {
99-
weightConfigRepository.deleteByExerciseId(exerciseId);
100-
CQIConfig.Weights w = cqiConfig.getWeights();
101-
102-
log.info("Reset CQI weights to defaults for exercise {}", exerciseId);
103-
104-
return ResponseEntity.ok(new CqiWeightsDTO(
105-
w.getEffort(), w.getLoc(), w.getTemporal(), w.getOwnership(), true));
58+
log.info("DELETE cqi-weights for exerciseId={}", exerciseId);
59+
return ResponseEntity.ok(cqiWeightService.resetWeights(exerciseId));
10660
}
10761
}

src/main/webapp/src/components/CqiWeightsPanel.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Input } from '@/components/ui/input';
77
import { Label } from '@/components/ui/label';
88
import { Badge } from '@/components/ui/badge';
99
import { toast } from '@/hooks/use-toast';
10-
import { Settings, RotateCcw, ChevronRight } from 'lucide-react';
10+
import { Settings, RotateCcw } from 'lucide-react';
1111
import type { CqiWeightsDTO } from '@/app/generated';
1212
import { cqiWeightsApi } from '@/lib/apiClient';
1313

@@ -34,13 +34,13 @@ function weightReducer(state: WeightState, action: WeightAction): WeightState {
3434
const clamp = (v: number) => Math.max(0, Math.min(100, v));
3535
switch (action.type) {
3636
case 'SET_EFFORT':
37-
return { ...state, effort: clamp(action.value) };
37+
return Object.assign({}, state, { effort: clamp(action.value) });
3838
case 'SET_LOC':
39-
return { ...state, loc: clamp(action.value) };
39+
return Object.assign({}, state, { loc: clamp(action.value) });
4040
case 'SET_TEMPORAL':
41-
return { ...state, temporal: clamp(action.value) };
41+
return Object.assign({}, state, { temporal: clamp(action.value) });
4242
case 'SET_OWNERSHIP':
43-
return { ...state, ownership: clamp(action.value) };
43+
return Object.assign({}, state, { ownership: clamp(action.value) });
4444
case 'RESET':
4545
return action.state;
4646
}

0 commit comments

Comments
 (0)