Repository: ls1intum/prompt2
Pull request #405: `Team Allocation`: Endpoints for skills and teams for Tease Integration
Author: login: mathildeshagl
Labels: name: schau mi o
description: Translation: Ready to review
Created: 2025-04-15T10:16:20Z | Merged: 2025-04-18T07:23:07Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added new endpoints to retrieve skills and teams for a specific course phase.
  - Responses include structured details for both skills and teams.

- **Bug Fixes**
  - Improved error handling for invalid course phase identifiers and internal server errors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/servers/team_allocation/tease/router.go b/servers/team_allocation/tease/router.go
index 98e01c178..09856acff 100644
--- a/servers/team_allocation/tease/router.go
+++ b/servers/team_allocation/tease/router.go
@@ -20,6 +20,8 @@ func setupTeaseRouter(routerGroup *gin.RouterGroup, authMiddleware func(allowedR
 	// course phase specific endpoints
 	teaseCoursePhaseRouter := teaseRouter.Group("/course_phase/:coursePhaseID")
 	teaseCoursePhaseRouter.GET("/students", authMiddleware(promptSDK.PromptAdmin, promptSDK.CourseLecturer), getTeaseStudentsForCoursePhase)
+	teaseCoursePhaseRouter.GET("/skills", authMiddleware(promptSDK.PromptAdmin, promptSDK.CourseLecturer), getTeaseSkillsByCoursePhase)
+	teaseCoursePhaseRouter.GET("/projects", authMiddleware(promptSDK.PromptAdmin, promptSDK.CourseLecturer), getTeaseTeamsByCoursePhase)
 }
 
 func getAllCoursePhases(c *gin.Context) {
@@ -67,6 +69,40 @@ func getTeaseStudentsForCoursePhase(c *gin.Context) {
 	c.JSON(http.StatusOK, students)
 }
 
+func getTeaseSkillsByCoursePhase(c *gin.Context) {
+	coursePhaseID, err := uuid.Parse(c.Param("coursePhaseID"))
+	if err != nil {
+		log.Error("Error parsing coursePhaseID: ", err)
+		handleError(c, http.StatusBadRequest, err)
+		return
+	}
+
+	skills, err := GetTeaseSkillsByCoursePhase(c, coursePhaseID)
+	if err != nil {
+		handleError(c, http.StatusInternalServerError, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, skills)
+}
+
+func getTeaseTeamsByCoursePhase(c *gin.Context) {
+	coursePhaseID, err := uuid.Parse(c.Param("coursePhaseID"))
+	if err != nil {
+		log.Error("Error parsing coursePhaseID: ", err)
+		handleError(c, http.StatusBadRequest, err)
+		return
+	}
+
+	teams, err := GetTeaseTeamsByCoursePhase(c, coursePhaseID)
+	if err != nil {
+		handleError(c, http.StatusInternalServerError, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, teams)
+}
+
 func handleError(c *gin.Context, statusCode int, err error) {
 	c.JSON(statusCode, gin.H{"error": err.Error()})
 }
diff --git a/servers/team_allocation/tease/service.go b/servers/team_allocation/tease/service.go
index 25f6f8ea7..0eb7a194f 100644
--- a/servers/team_allocation/tease/service.go
+++ b/servers/team_allocation/tease/service.go
@@ -172,3 +172,29 @@ func getTeaseSkillLevel(ctx context.Context, coursePhaseID uuid.UUID, coursePart
 
 	return teaseDTO.GetTeaseStudentSkillResponseFromDBModel(skills), nil
 }
+
+func GetTeaseSkillsByCoursePhase(ctx context.Context, coursePhaseID uuid.UUID) ([]teaseDTO.Skill, error) {
+	ctxWithTimeout, cancel := db.GetTimeoutContext(ctx)
+	defer cancel()
+
+	skills, err := TeaseServiceSingleton.queries.GetSkillsByCoursePhase(ctxWithTimeout, coursePhaseID)
+	if err != nil {
+		log.Error("failed to get skills for course phase: ", err)
+		return nil, err
+	}
+
+	return teaseDTO.GetTeaseSkillFromDBModel(skills), nil
+}
+
+func GetTeaseTeamsByCoursePhase(ctx context.Context, coursePhaseID uuid.UUID) ([]teaseDTO.TeaseTeam, error) {
+	ctxWithTimeout, cancel := db.GetTimeoutContext(ctx)
+	defer cancel()
+
+	teams, err := TeaseServiceSingleton.queries.GetTeamsByCoursePhase(ctxWithTimeout, coursePhaseID)
+	if err != nil {
+		log.Error("failed to get teams for course phase: ", err)
+		return nil, err
+	}
+
+	return teaseDTO.GetTeaseTeamResponseFromDBModel(teams), nil
+}
diff --git a/servers/team_allocation/tease/teaseDTO/teaseSkill.go b/servers/team_allocation/tease/teaseDTO/teaseSkill.go
new file mode 100644
index 000000000..f8ec127d9
--- /dev/null
+++ b/servers/team_allocation/tease/teaseDTO/teaseSkill.go
@@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	Title       string `json:"title"`
+	ID          string `json:"id"`
+	Description string `json:"description"`
+}
+
+func GetTeaseSkillFromDBModel(skills []db.Skill) []Skill {
+	teaseSkills := make([]Skill, 0, len(skills))
+	for _, skill := range skills {
+		teaseSkill := Skill{
+			Title:       skill.Name,
+			ID:          skill.ID.String(),
+			Description: "",
+		}
+		teaseSkills = append(teaseSkills, teaseSkill)
+	}
+	return teaseSkills
+}
diff --git a/servers/team_allocation/tease/teaseDTO/teaseTeam.go b/servers/team_allocation/tease/teaseDTO/teaseTeam.go
new file mode 100644
index 000000000..1840c4401
--- /dev/null
+++ b/servers/team_allocation/tease/teaseDTO/teaseTeam.go
@@ -0,0 +1,22 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type TeaseTeam struct {
+	TeamID   string `json:"id"`
+	TeamName string `json:"name"`
+}
+
+func GetTeaseTeamResponseFromDBModel(teams []db.Team) []TeaseTeam {
+	teaseTeams := make([]TeaseTeam, 0, len(teams))
+	for _, team := range teams {
+		teaseTeam := TeaseTeam{
+			TeamID:   team.ID.String(),
+			TeamName: team.Name,
+		}
+		teaseTeams = append(teaseTeams, teaseTeam)
+	}
+	return teaseTeams
+}


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseTeam.go (1)</summary><blockquote>

`12-22`: **Consider pre-allocating slice capacity for better performance**

The conversion function works correctly, but for better performance with large datasets, consider pre-allocating the slice capacity based on the input length.


```diff
func GetTeaseTeamResponseFromDBModel(teams []db.Team) []TeaseTeam {
-	var teaseTeams []TeaseTeam
+	teaseTeams := make([]TeaseTeam, 0, len(teams))
	for _, team := range teams {
		teaseTeam := TeaseTeam{
			TeamID:   team.ID.String(),
			TeamName: team.Name,
		}
		teaseTeams = append(teaseTeams, teaseTeam)
	}
	return teaseTeams
}
```

</blockquote></details>
<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseSkill.go (2)</summary><blockquote>

`7-11`: **Consider consistent naming convention across DTOs**

The `SkillResponse` struct is well-defined, but it follows a different naming convention than `TeaseTeam` in the other file. Consider using a consistent naming pattern, either `TeaseSkill` or `TeamResponse`.

---

`13-24`: **Empty skill description and missing capacity pre-allocation**

Two observations:
1. `SkillDescription` is always set to an empty string. If this field is not used, consider removing it or documenting why it's included but empty.
2. For better performance with large datasets, consider pre-allocating the slice capacity.


```diff
func GetTeaseSkillResponseFromDBModel(skills []db.Skill) []SkillResponse {
-	var teaseSkills []SkillResponse
+	teaseSkills := make([]SkillResponse, 0, len(skills))
	for _, skill := range skills {
		teaseSkill := SkillResponse{
			SkillTitle:       skill.Name,
			SkillID:          skill.ID.String(),
			SkillDescription: "",
		}
		teaseSkills = append(teaseSkills, teaseSkill)
	}
	return teaseSkills
}
```

</blockquote></details>
<details>
<summary>servers/team_allocation/tease/router.go (2)</summary><blockquote>

`55-71`: **Pass request context instead of the Gin context**

The service function only needs the request context, not the full Gin context. Consider extracting just the request context to pass to the service function.


```diff
-	skills, err := GetTeaseSkillsByCoursePhase(c, authHeader, coursePhaseID)
+	skills, err := GetTeaseSkillsByCoursePhase(c.Request.Context(), authHeader, coursePhaseID)
```

---

`73-89`: **Pass request context instead of the Gin context**

Similar to the skills handler, extract just the request context to pass to the service function.


```diff
-	teams, err := GetTeaseTeamsByCoursePhase(c, authHeader, coursePhaseID)
+	teams, err := GetTeaseTeamsByCoursePhase(c.Request.Context(), authHeader, coursePhaseID)
```

</blockquote></details>
<details>
<summary>servers/team_allocation/tease/service.go (2)</summary><blockquote>

`102-113`: **Unused authHeader parameter**

The `authHeader` parameter is not used in this function. Either remove it if it's not needed or document why it's included for future use.


```diff
-func GetTeaseSkillsByCoursePhase(ctx context.Context, authHeader string, coursePhaseID uuid.UUID) ([]teaseDTO.SkillResponse, error) {
+func GetTeaseSkillsByCoursePhase(ctx context.Context, coursePhaseID uuid.UUID) ([]teaseDTO.SkillResponse, error) {
	ctxWithTimeout, cancel := db.GetTimeoutContext(ctx)
	defer cancel()

	skills, err := TeaseServiceSingleton.queries.GetSkillsByCoursePhase(ctxWithTimeout, coursePhaseID)
	if err != nil {
		log.Error("failed to get skills for course phase: ", err)
		return nil, err
	}

	return teaseDTO.GetTeaseSkillResponseFromDBModel(skills), nil
}
```

If you need to keep it for future use, consider adding a comment explaining that:

```diff
func GetTeaseSkillsByCoursePhase(ctx context.Context, authHeader string, coursePhaseID uuid.UUID) ([]teaseDTO.SkillResponse, error) {
+	// authHeader is reserved for future use when authentication check is needed
	ctxWithTimeout, cancel := db.GetTimeoutContext(ctx)
	defer cancel()
```

---

`115-126`: **Unused authHeader parameter**

Similarly, the `authHeader` parameter is not used in this function either. Consider removing it or documenting its purpose.


```diff
-func GetTeaseTeamsByCoursePhase(ctx context.Context, authHeader string, coursePhaseID uuid.UUID) ([]teaseDTO.TeaseTeam, error) {
+func GetTeaseTeamsByCoursePhase(ctx context.Context, coursePhaseID uuid.UUID) ([]teaseDTO.TeaseTeam, error) {
	ctxWithTimeout, cancel := db.GetTimeoutContext(ctx)
	defer cancel()

	teams, err := TeaseServiceSingleton.queries.GetTeamsByCoursePhase(ctxWithTimeout, coursePhaseID)
	if err != nil {
		log.Error("failed to get teams for course phase: ", err)
		return nil, err
	}

	return teaseDTO.GetTeaseTeamResponseFromDBModel(teams), nil
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b41bea5864b8fc81ce0d7b5f32f5dda747ec7e8d and 3bbd3b5e3e1abc4a9507390d5fac5a99ada52398.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `servers/team_allocation/tease/router.go` (3 hunks)
* `servers/team_allocation/tease/service.go` (1 hunks)
* `servers/team_allocation/tease/teaseDTO/teaseSkill.go` (1 hunks)
* `servers/team_allocation/tease/teaseDTO/teaseTeam.go` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>servers/team_allocation/tease/router.go (1)</summary>

```
Learnt from: niclasheun
PR: ls1intum/prompt2#305
File: servers/intro_course/infrastructureSetup/router.go:27-49
Timestamp: 2025-03-22T12:38:08.069Z
Learning: In the intro_course project, the coursePhaseID parameter is already included in the base router path from the main.go file (api := router.Group("intro-course/api/course_phase/:coursePhaseID")), so routes defined in module routers don't need to explicitly include it in their paths.
```

</details>

</details><details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>servers/team_allocation/tease/router.go (1)</summary><blockquote>

<details>
<summary>servers/team_allocation/tease/service.go (2)</summary>

* `GetTeaseSkillsByCoursePhase` (102-113)
* `GetTeaseTeamsByCoursePhase` (115-126)

</details>

</blockquote></details>
<details>
<summary>servers/team_allocation/tease/service.go (3)</summary><blockquote>

<details>
<summary>servers/team_allocation/db/sqlc/getTimeoutContext.go (1)</summary>

* `GetTimeoutContext` (10-12)

</details>
<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseSkill.go (1)</summary>

* `GetTeaseSkillResponseFromDBModel` (13-24)

</details>
<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseTeam.go (2)</summary>

* `TeaseTeam` (7-10)
* `GetTeaseTeamResponseFromDBModel` (12-22)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseTeam.go (1)</summary>

`7-10`: **Clean and well-structured DTO definition**

The `TeaseTeam` struct is well-designed with clear field names and appropriate JSON tags. The naming is descriptive and follows conventions.

</details>
<details>
<summary>servers/team_allocation/tease/router.go (1)</summary>

`22-24`: **LGTM: New endpoints added with proper authorization**

The new endpoints are well-structured and properly protected with authorization middleware for the appropriate roles.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T11:30:51Z
user: login: niclasheun
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T06:39:17Z
user: login: niclasheun
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T22:35:58Z
user: login: mathildeshagl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T09:38:45Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseSkill.go (2)</summary><blockquote>

`7-11`: **Consider renaming this struct to clarify its purpose.**

Based on past review comments, there seems to be some confusion about naming. Consider whether "Skill" is the most descriptive name for this struct, or if a name like "TeaseSkill" or "SkillDTO" would better communicate its purpose in the codebase.



Previous reviewer niclasheun commented: "Just looking over this again, are you sure that SkillResponse is the right name? SkillResponse is in my opinion the answer a student gave to a certain Skill. But this is all available Skills. So I'm not sure if SkillResponse is the most fitting word."

---

`14-14`: **The use of make is appropriate here.**

This line correctly pre-allocates capacity for the slice, which is an efficient approach when the final size is known in advance. It avoids unnecessary reallocations during append operations.



Previous reviewer niclasheun commented: "Same here with the make"

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>servers/team_allocation/tease/teaseDTO/teaseSkill.go (2)</summary><blockquote>

`7-11`: **Consider adding documentation to the Skill struct.**

Adding documentation comments that describe the purpose of this struct and its fields would improve code readability and maintainability. This is especially helpful since this struct serves as a data transfer object (DTO) for external integrations.

```diff
+// Skill represents a skill entity for the Tease integration API
 type Skill struct {
+    // SkillTitle is the name of the skill
     SkillTitle       string `json:"skillTitle"`
+    // SkillID is the unique identifier of the skill
     SkillID          string `json:"skillId"`
+    // SkillDescription contains additional information about the skill
     SkillDescription string `json:"skillDescription"`
 }
```

---

`13-24`: **Add documentation and explain empty SkillDescription.**

The function lacks documentation explaining its purpose. Also, it's unclear why `SkillDescription` is explicitly set to an empty string. Is this field not available in the database model, or is it intentionally omitted?

```diff
+// GetTeaseSkillFromDBModel converts database Skill models to Tease API DTOs
+// Note: SkillDescription is set to empty string as this information is not available in the database model
 func GetTeaseSkillFromDBModel(skills []db.Skill) []Skill {
     teaseSkills := make([]Skill, 0, len(skills))
     for _, skill := range skills {
         teaseSkill := Skill{
             SkillTitle:       skill.Name,
             SkillID:          skill.ID.String(),
             SkillDescription: "",
         }
         teaseSkills = append(teaseSkills, teaseSkill)
     }
     return teaseSkills
 }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 4f382f750525ffc5d6846517032595726626120d and 361376d4200b257c89830c285171b861cd9a8c65.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `servers/team_allocation/tease/service.go` (1 hunks)
* `servers/team_allocation/tease/teaseDTO/teaseSkill.go` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* servers/team_allocation/tease/service.go

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (10)</summary>

* GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
* GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
* GitHub Check: build-servers / assessment / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-assessment
* GitHub Check: build-servers / team-allocation / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-team-allocation
* GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
* GitHub Check: build-servers / assessment / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-assessment
* GitHub Check: build-servers / team-allocation / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-team-allocation
* GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
* GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
* GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T09:40:06Z
user: login: rappm
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T20:22:09Z
user: login: rappm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T20:24:51Z
user: login: niclasheun
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T21:53:43Z
user: login: niclasheun
body: Please strictly follow the API spec
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T21:57:31Z
user: login: mathildeshagl
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T06:30:01Z
user: login: niclasheun
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T06:52:40Z

Review comments:
diff_hunk: @@ -0,0 +1,22 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type TeaseTeam struct {
+	TeamID   string `json:"id"`
+	TeamName string `json:"name"`
+}
+
+func GetTeaseTeamResponseFromDBModel(teams []db.Team) []TeaseTeam {
+	var teaseTeams []TeaseTeam
path: servers/team_allocation/tease/teaseDTO/teaseTeam.go
user: login: niclasheun
body: Please use the make([]TeaseTeam, 0, ...) 

This is a bit more performant and in the case of no teams this will return an empty array vs null in your current case 
created_at: 2025-04-16T06:35:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type SkillResponse struct {
+	SkillTitle       string `json:"skillTitle"`
+	SkillID          string `json:"skillId"`
+	SkillDescription string `json:"skillDescription"`
+}
+
+func GetTeaseSkillResponseFromDBModel(skills []db.Skill) []SkillResponse {
+	var teaseSkills []SkillResponse
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: niclasheun
body: Same here with the make
created_at: 2025-04-16T06:36:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,6 +52,42 @@ func getAllCoursePhases(c *gin.Context) {
 	c.JSON(http.StatusOK, teasePhases)
 }
 
+func getTeaseSkillsByCoursePhase(c *gin.Context) {
+	coursePhaseID, err := uuid.Parse(c.Param("coursePhaseID"))
+	if err != nil {
+		log.Error("Error parsing coursePhaseID: ", err)
+		handleError(c, http.StatusBadRequest, err)
+		return
+	}
+	authHeader := c.GetHeader("Authorization")
+
+	skills, err := GetTeaseSkillsByCoursePhase(c, authHeader, coursePhaseID)
+	if err != nil {
+		handleError(c, http.StatusInternalServerError, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, skills)
+}
+
+func getTeaseTeamsByCoursePhase(c *gin.Context) {
+	coursePhaseID, err := uuid.Parse(c.Param("coursePhaseID"))
+	if err != nil {
+		log.Error("Error parsing coursePhaseID: ", err)
+		handleError(c, http.StatusBadRequest, err)
+		return
+	}
+	authHeader := c.GetHeader("Authorization")
path: servers/team_allocation/tease/router.go
user: login: niclasheun
body: Why are you extracting the authHeader here and passing it to the function ? You do not seem to use it (and you do not need to use it for these endpoints) 
created_at: 2025-04-16T06:38:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,6 +52,42 @@ func getAllCoursePhases(c *gin.Context) {
 	c.JSON(http.StatusOK, teasePhases)
 }
 
+func getTeaseSkillsByCoursePhase(c *gin.Context) {
+	coursePhaseID, err := uuid.Parse(c.Param("coursePhaseID"))
+	if err != nil {
+		log.Error("Error parsing coursePhaseID: ", err)
+		handleError(c, http.StatusBadRequest, err)
+		return
+	}
+	authHeader := c.GetHeader("Authorization")
path: servers/team_allocation/tease/router.go
user: login: niclasheun
body: Same here with the authHeader
created_at: 2025-04-16T06:38:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type SkillResponse struct {
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: niclasheun
body: Just looking over this again, are you sure that SkillResponse is the right name? 

SkillResponse is in my opinion the the answer a student gave to a certain Skill. But this is all available Skills. So I'm not sure if SkillResponse is the most fitting word. 
created_at: 2025-04-16T22:35:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type SkillResponse struct {
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: mathildeshagl
body: Fair point
created_at: 2025-04-17T09:38:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	SkillTitle       string `json:"skillTitle"`
+	SkillID          string `json:"skillId"`
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: rappm
body: ```suggestion
	SkillID          string `json:"skillID"`
```
created_at: 2025-04-17T20:21:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	SkillTitle       string `json:"skillTitle"`
+	SkillID          string `json:"skillId"`
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: rappm
body: Just to be consistent with the other IDs we use in Prompt, apart from that LGTM
created_at: 2025-04-17T20:24:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	SkillTitle       string `json:"skillTitle"`
+	SkillID          string `json:"skillId"`
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: niclasheun
body: No! This is not indetended to be consistent, but offer the data format expected by tease. But this also does not comply with tease
created_at: 2025-04-17T21:53:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	SkillTitle       string `json:"skillTitle"`
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: niclasheun
body: Please follow the https://github.com/ls1intum/tease/blob/main/client/docs/openapi_spec.yaml
It states that it requires an id, title and description
created_at: 2025-04-17T21:54:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+package teaseDTO
+
+import (
+	db "github.com/ls1intum/prompt2/servers/team_allocation/db/sqlc"
+)
+
+type Skill struct {
+	SkillTitle       string `json:"skillTitle"`
path: servers/team_allocation/tease/teaseDTO/teaseSkill.go
user: login: mathildeshagl
body: Should be fixed now🫠
created_at: 2025-04-18T06:30:01Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-15T10:16:26Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

"""
## Walkthrough

This change introduces two new GET endpoints under the `/tease/course_phase/:coursePhaseID` route for retrieving skills and teams associated with a given course phase. The endpoints are protected by authorization middleware for specific roles. Supporting service functions are added to fetch the relevant data from the database, and new data transfer object (DTO) types are defined for serializing the responses. Error handling and logging follow established patterns, and the codebase is updated with new files for the DTOs.

## Changes

| File(s)                                               | Change Summary                                                                                                               |
|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| servers/team_allocation/tease/router.go               | Adds two GET endpoints (`/skills` and `/projects`) under `/tease/course_phase/:coursePhaseID` with handlers and authorization.|
| servers/team_allocation/tease/service.go              | Adds service functions to fetch skills and teams by course phase from the database, with context and error handling.         |
| servers/team_allocation/tease/teaseDTO/teaseSkill.go  | Adds `Skill` struct and conversion function from DB skill models to DTOs.                                                    |
| servers/team_allocation/tease/teaseDTO/teaseTeam.go   | Adds `TeaseTeam` struct and conversion function from DB team models to DTOs.                                                 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant Router
    participant Service
    participant DB

    Client->>Router: GET /tease/course_phase/:coursePhaseID/skills (with Authorization)
    Router->>Service: getTeaseSkillsByCoursePhase()
    Service->>DB: Query skills by coursePhaseID
    DB-->>Service: Return skills
    Service-->>Router: Return Skill DTOs
    Router-->>Client: 200 OK + JSON skills

    Client->>Router: GET /tease/course_phase/:coursePhaseID/projects (with Authorization)
    Router->>Service: getTeaseTeamsByCoursePhase()
    Service->>DB: Query teams by coursePhaseID
    DB-->>Service: Return teams
    Service-->>Router: Return TeaseTeam DTOs
    Router-->>Client: 200 OK + JSON teams
```

## Possibly related PRs

- ls1intum/prompt2#401: Adds endpoints and service functions for fetching students by course phase in the same `tease` package, complementing the skills and teams endpoints introduced here.

## Suggested reviewers

- rappm

## Poem

> In the warren of code, new routes appear,  
> For skills and teams, the endpoints are clear.  
> With DTOs shiny and queries that hop,  
> The data now flows from bottom to top.  
> 🐇✨  
> So here’s to new features, with teamwork and skill—  
> This rabbit approves, with a hop and a thrill!
"""

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3442a80dba66c614b86183ace78e2ac4e7a853a2 and 53855674a965f3fdd879667d9374a1ee3d81b5a0.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `servers/team_allocation/tease/teaseDTO/teaseSkill.go` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* servers/team_allocation/tease/teaseDTO/teaseSkill.go

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (10)</summary>

* GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
* GitHub Check: build-clients / clients-base / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-clients-base
* GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course
* GitHub Check: build-servers / team-allocation / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-team-allocation
* GitHub Check: build-servers / team-allocation / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-team-allocation
* GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
* GitHub Check: build-servers / assessment / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-assessment
* GitHub Check: build-servers / assessment / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-assessment
* GitHub Check: build-servers / core / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-core
* GitHub Check: build-servers / intro-course / Build Docker Image for ghcr.io/ls1intum/prompt2/prompt-server-intro-course

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ls1intum/prompt2&utm_content=405):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: niclasheun
created_at: 2025-04-16T06:39:34Z
body: Looks good. Just some details 
author_association: CONTRIBUTOR
user: login: niclasheun
created_at: 2025-04-17T17:44:42Z
body: Could you please fist fix the merge conflict. (Otherwise I need to reapprove anyway) 
author_association: CONTRIBUTOR
user: login: mathildeshagl
created_at: 2025-04-17T19:19:04Z
body: > Could you please fist fix the merge conflict. (Otherwise I need to reapprove anyway)

is fixed @niclasheun 👍🏻
author_association: CONTRIBUTOR
user: login: niclasheun
created_at: 2025-04-18T06:52:55Z
body: LGTM. Thanks!
author_association: CONTRIBUTOR