Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
650775a
style: change font and polish UI
markstockhausen Jan 27, 2026
79e45a2
style: update font weight
markstockhausen Jan 27, 2026
430e1c2
style: conform to Prettier formatting
markstockhausen Feb 3, 2026
73a402b
Implement optimized Scheduling Service and Pipeline Logic
markstockhausen Feb 3, 2026
8850cf6
style: update comments
markstockhausen Feb 3, 2026
fe676c3
Implement AI suggestions
markstockhausen Feb 9, 2026
b199432
Update Axios, Optimise scheduling pipeline
markstockhausen Feb 9, 2026
67d6e58
Optimise pipeline logic and clean up code
markstockhausen Feb 10, 2026
60d7f66
Optimise pipeline
markstockhausen Feb 10, 2026
6251a3a
Update logic for bidirectional relationship types
markstockhausen Feb 10, 2026
e683410
Streamline implementation
markstockhausen Feb 10, 2026
141bebd
Conform to Prettier formatting
markstockhausen Feb 10, 2026
64c4f20
Implement AI suggestions
markstockhausen Feb 17, 2026
d863e77
Further AI suggestions
markstockhausen Feb 17, 2026
846fd3d
Merge branch 'main' into feature/implement-scheduling-service
markstockhausen Feb 17, 2026
7916901
feat: add dashboard with contribution heatmap, badges, and session su…
markstockhausen Feb 17, 2026
4b03244
style: fix Prettier formatting
markstockhausen Feb 17, 2026
2190551
Update heatmap
markstockhausen Feb 24, 2026
785d9fa
Conform to Prettier formatting
markstockhausen Feb 24, 2026
2704820
Fix security audit
markstockhausen Feb 24, 2026
d675645
Merge branch 'main' into feature/personal-dashboard
markstockhausen Feb 27, 2026
11585f8
style: change font and polish UI
markstockhausen Jan 27, 2026
ad5044c
Implement AI suggestions
markstockhausen Feb 17, 2026
ad58faa
Simplify relationship saving
markstockhausen Feb 27, 2026
9442bf4
Conform to Prettier formatting
markstockhausen Feb 27, 2026
f9c89cc
Sync package-lock with minimatch
markstockhausen Feb 27, 2026
ff8f807
Update package-lock, implement AI suggestions
markstockhausen Feb 27, 2026
912c97c
Update undo logic, Implement Copilot suggestions
markstockhausen Feb 27, 2026
3c5fdce
Resolve dependency conflicts
markstockhausen Feb 27, 2026
2b3c8c8
Optimize contributor stats query
markstockhausen Mar 4, 2026
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
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.CreateCompetencyRelationshipRequest;
import de.tum.cit.memo.entity.CompetencyRelationship;
import de.tum.cit.memo.service.CompetencyRelationshipService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* Controller for competency relationship CRUD.
* For creating relationships and voting, use {@link SchedulingController}.
*/
@RestController
@RequestMapping("/api/competency-relationships")
@RequiredArgsConstructor
Expand All @@ -27,13 +26,6 @@ public class CompetencyRelationshipController {

private final CompetencyRelationshipService relationshipService;

@PostMapping
@Operation(summary = "Create a new competency relationship")
public ResponseEntity<CompetencyRelationship> createRelationship(@Valid @RequestBody CreateCompetencyRelationshipRequest request) {
CompetencyRelationship relationship = relationshipService.createRelationship(request);
return ResponseEntity.status(HttpStatus.CREATED).body(relationship);
}

@GetMapping("/{id}")
@Operation(summary = "Get competency relationship by ID")
public ResponseEntity<CompetencyRelationship> getRelationshipById(@PathVariable String id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.ContributorStatsResponse;
import de.tum.cit.memo.service.ContributorStatsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
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;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Tag(name = "Contributor Stats", description = "Endpoints for contributor statistics and gamification")
public class ContributorStatsController {

private final ContributorStatsService contributorStatsService;

@GetMapping("/{userId}/stats")
@Operation(summary = "Get contributor statistics", description = "Returns contribution stats including total votes, streaks, daily counts for heatmap, "
+ "and earned badge IDs.")
public ResponseEntity<ContributorStatsResponse> getContributorStats(@PathVariable String userId) {
ContributorStatsResponse stats = contributorStatsService.getStats(userId);
return ResponseEntity.ok(stats);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package de.tum.cit.memo.controller;

import de.tum.cit.memo.dto.RelationshipTaskResponse;
import de.tum.cit.memo.dto.VoteRequest;
import de.tum.cit.memo.dto.VoteResponse;
import de.tum.cit.memo.service.SchedulingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/scheduling")
@RequiredArgsConstructor
@Tag(name = "Scheduling", description = "Endpoints for competency mapping scheduling")
public class SchedulingController {

private final SchedulingService schedulingService;

@GetMapping("/next-relationship")
@Operation(summary = "Get next relationship to vote on", description = "Returns a competency pair for the user to map. Uses coverage (70%) and consensus (30%) pipelines. Returns 204 if no tasks remain.")
public ResponseEntity<RelationshipTaskResponse> getNextRelationship(
@RequestHeader("X-User-Id") String userId) {
return schedulingService.getNextTask(userId)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.noContent().build());
}

@PostMapping("/vote")
@Operation(summary = "Submit a vote on a relationship", description = "Records the user's vote on a competency relationship. Accepts either a relationshipId or originId+destinationId pair (for swapped direction). MATCHES and UNRELATED votes are bidirectional.")
Comment thread
markstockhausen marked this conversation as resolved.
Outdated
public ResponseEntity<VoteResponse> submitVote(
@RequestHeader("X-User-Id") String userId,
@Valid @RequestBody VoteRequest request) {
VoteResponse response = schedulingService.submitVote(userId, request);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContributorStatsResponse {

private int totalVotes;
private int currentStreak;
private int longestStreak;
private List<DailyCount> dailyCounts;
private List<String> earnedBadges;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class DailyCount {
private LocalDate date;
private int count;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RelationshipTaskResponse {

private String relationshipId;
private CompetencyInfo origin;
private CompetencyInfo destination;
private String pipeline;
private VoteCounts currentVotes;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class CompetencyInfo {
private String id;
private String title;
private String description;
}
}
18 changes: 18 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteCounts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteCounts {

private int assumes;
private int extendsRelation;
private int matches;
private int unrelated;
}
36 changes: 36 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package de.tum.cit.memo.dto;

import de.tum.cit.memo.enums.RelationshipType;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteRequest {

/**
* Relationship ID (used directly when direction is not swapped).
* Optional if originId + destinationId are provided instead.
*/
private String relationshipId;
Comment thread
markstockhausen marked this conversation as resolved.
Outdated

/**
* Origin competency ID (used when direction is swapped).
* Must be provided together with destinationId.
*/
private String originId;

/**
* Destination competency ID (used when direction is swapped).
* Must be provided together with originId.
*/
private String destinationId;

@NotNull(message = "Relationship type is required")
private RelationshipType relationshipType;
}
17 changes: 17 additions & 0 deletions server/src/main/java/de/tum/cit/memo/dto/VoteResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package de.tum.cit.memo.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class VoteResponse {

private boolean success;
private VoteCounts updatedVotes;
private double newEntropy;
}
3 changes: 3 additions & 0 deletions server/src/main/java/de/tum/cit/memo/entity/Competency.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,7 @@ public class Competency {
@CreationTimestamp
@Column(nullable = false, updatable = false)
private Instant createdAt;

@Column(nullable = false)
private int degree;
}
Loading