Skip to content

Commit 640bcf3

Browse files
committed
feat: add manual interview count update via API and MCP
Adds PATCH /api/v1/dashboard/interview-count endpoint and Update-Interview-Count MCP tool so users can correct the cumulative interview counter when the automatic detection misses transitions. https://claude.ai/code/session_015kfgrc2oHA881RT3bEYHZ6
1 parent 661d0b1 commit 640bcf3

4 files changed

Lines changed: 77 additions & 2 deletions

File tree

src/main/java/com/jobtracker/controller/DashboardController.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package com.jobtracker.controller;
22

33
import com.jobtracker.dto.dashboard.DashboardSummaryResponse;
4+
import com.jobtracker.dto.dashboard.UpdateInterviewCountRequest;
45
import com.jobtracker.service.DashboardService;
6+
import com.jobtracker.service.InterviewMetricsService;
7+
import com.jobtracker.util.SecurityUtils;
58
import io.swagger.v3.oas.annotations.Operation;
69
import io.swagger.v3.oas.annotations.media.Content;
710
import io.swagger.v3.oas.annotations.media.Schema;
811
import io.swagger.v3.oas.annotations.responses.ApiResponse;
912
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import jakarta.validation.Valid;
1014
import org.springframework.http.ResponseEntity;
1115
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PatchMapping;
17+
import org.springframework.web.bind.annotation.RequestBody;
1218
import org.springframework.web.bind.annotation.RequestMapping;
1319
import org.springframework.web.bind.annotation.RestController;
1420

@@ -18,9 +24,15 @@
1824
public class DashboardController {
1925

2026
private final DashboardService dashboardService;
27+
private final InterviewMetricsService interviewMetricsService;
28+
private final SecurityUtils securityUtils;
2129

22-
public DashboardController(DashboardService dashboardService) {
30+
public DashboardController(DashboardService dashboardService,
31+
InterviewMetricsService interviewMetricsService,
32+
SecurityUtils securityUtils) {
2333
this.dashboardService = dashboardService;
34+
this.interviewMetricsService = interviewMetricsService;
35+
this.securityUtils = securityUtils;
2436
}
2537

2638
@Operation(
@@ -36,4 +48,19 @@ public DashboardController(DashboardService dashboardService) {
3648
public ResponseEntity<DashboardSummaryResponse> getSummary() {
3749
return ResponseEntity.ok(dashboardService.getSummary());
3850
}
51+
52+
@Operation(
53+
summary = "Update interview count",
54+
description = "Manually sets the cumulative interview count for the authenticated user",
55+
responses = {
56+
@ApiResponse(responseCode = "204", description = "Count updated"),
57+
@ApiResponse(responseCode = "400", description = "Invalid count value"),
58+
@ApiResponse(responseCode = "401", description = "Not authenticated")
59+
}
60+
)
61+
@PatchMapping("/interview-count")
62+
public ResponseEntity<Void> updateInterviewCount(@Valid @RequestBody UpdateInterviewCountRequest request) {
63+
interviewMetricsService.setInterviewCount(securityUtils.getCurrentUserId(), request.count());
64+
return ResponseEntity.noContent().build();
65+
}
3966
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.jobtracker.dto.dashboard;
2+
3+
import jakarta.validation.constraints.Min;
4+
5+
public record UpdateInterviewCountRequest(
6+
@Min(0) long count
7+
) {}

src/main/java/com/jobtracker/mcp/tools/McpAnalyticsTools.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.jobtracker.mapper.ApplicationMapper;
1010
import com.jobtracker.repository.ApplicationRepository;
1111
import com.jobtracker.service.ApplicationService;
12+
import com.jobtracker.service.InterviewMetricsService;
1213
import com.jobtracker.util.SecurityUtils;
1314
import org.springaicommunity.mcp.annotation.McpTool;
1415
import org.springaicommunity.mcp.annotation.McpTool.McpAnnotations;
@@ -33,18 +34,39 @@ public class McpAnalyticsTools {
3334
private final ApplicationRepository applicationRepository;
3435
private final ApplicationMapper applicationMapper;
3536
private final ApplicationService applicationService;
37+
private final InterviewMetricsService interviewMetricsService;
3638
private final SecurityUtils securityUtils;
3739

3840
public McpAnalyticsTools(ApplicationRepository applicationRepository,
3941
ApplicationMapper applicationMapper,
4042
ApplicationService applicationService,
43+
InterviewMetricsService interviewMetricsService,
4144
SecurityUtils securityUtils) {
4245
this.applicationRepository = applicationRepository;
4346
this.applicationMapper = applicationMapper;
4447
this.applicationService = applicationService;
48+
this.interviewMetricsService = interviewMetricsService;
4549
this.securityUtils = securityUtils;
4650
}
4751

52+
@McpTool(
53+
name = "Update-Interview-Count",
54+
title = "Update Interview Count",
55+
description = "Manually sets the cumulative interview count for the current user. Use this when the automatic counter missed interviews or needs correction.",
56+
annotations = @McpAnnotations(
57+
title = "Update Interview Count",
58+
readOnlyHint = false,
59+
destructiveHint = false,
60+
idempotentHint = true,
61+
openWorldHint = false))
62+
@Transactional
63+
public String updateInterviewCount(
64+
@McpToolParam(required = true, description = "The new total interview count (must be >= 0)") long count) {
65+
UUID userId = securityUtils.getCurrentUserId();
66+
interviewMetricsService.setInterviewCount(userId, count);
67+
return "Interview count updated to " + count;
68+
}
69+
4870
@McpTool(
4971
name = "Get-Analytics",
5072
title = "Get Analytics",

src/main/java/com/jobtracker/service/InterviewMetricsService.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.jobtracker.entity.enums.ApplicationStatus;
88
import com.jobtracker.repository.InterviewEventRepository;
99
import com.jobtracker.repository.UserInterviewMetricsRepository;
10+
import com.jobtracker.repository.UserRepository;
1011
import org.springframework.stereotype.Service;
1112
import org.springframework.transaction.annotation.Transactional;
1213

@@ -29,11 +30,14 @@ public class InterviewMetricsService {
2930

3031
private final UserInterviewMetricsRepository metricsRepository;
3132
private final InterviewEventRepository eventRepository;
33+
private final UserRepository userRepository;
3234

3335
public InterviewMetricsService(UserInterviewMetricsRepository metricsRepository,
34-
InterviewEventRepository eventRepository) {
36+
InterviewEventRepository eventRepository,
37+
UserRepository userRepository) {
3538
this.metricsRepository = metricsRepository;
3639
this.eventRepository = eventRepository;
40+
this.userRepository = userRepository;
3741
}
3842

3943
public boolean isInterviewStatus(String status) {
@@ -77,6 +81,21 @@ public void recordStatusTransition(JobApplication application,
7781
eventRepository.save(event);
7882
}
7983

84+
@Transactional
85+
public void setInterviewCount(UUID userId, long count) {
86+
if (count < 0) throw new IllegalArgumentException("Interview count cannot be negative");
87+
UserInterviewMetrics metrics = metricsRepository.findByUser_Id(userId)
88+
.orElseGet(() -> {
89+
User user = userRepository.getReferenceById(userId);
90+
UserInterviewMetrics created = new UserInterviewMetrics();
91+
created.setUser(user);
92+
created.setInterviewCount(0);
93+
return created;
94+
});
95+
metrics.setInterviewCount(count);
96+
metricsRepository.save(metrics);
97+
}
98+
8099
@Transactional(readOnly = true)
81100
public long getInterviewCount(UUID userId) {
82101
return metricsRepository.findById(userId)

0 commit comments

Comments
 (0)