Skip to content

Commit a3ddae2

Browse files
authored
Merge pull request #64 from ls1intum/optimizations
Add temporary QA logs
2 parents b31acbe + 1ee5e54 commit a3ddae2

File tree

16 files changed

+364
-14
lines changed

16 files changed

+364
-14
lines changed

application-server/src/main/java/com/ase/angelos_kb_backend/controller/ChatController.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.ase.angelos_kb_backend.service.AngelosService;
1313
import com.ase.angelos_kb_backend.service.EventService;
1414
import com.ase.angelos_kb_backend.service.OrganisationService;
15+
import com.ase.angelos_kb_backend.service.QaLogService;
1516
import com.ase.angelos_kb_backend.service.StudyProgramService;
1617
import com.ase.angelos_kb_backend.util.JwtUtil;
1718

@@ -40,7 +41,7 @@ public class ChatController {
4041
private final StudyProgramService studyProgramService;
4142
private final OrganisationService organisationService;
4243
private final EventService eventService;
43-
44+
private final QaLogService qaLogService;
4445

4546
@Value("${angelos.username}")
4647
private String angelosUsername;
@@ -51,12 +52,20 @@ public class ChatController {
5152
@Value("${app.max-message-length}")
5253
private int maxMessageLength;
5354

54-
public ChatController(JwtUtil jwtUtil, AngelosService angelosService, StudyProgramService studyProgramService, OrganisationService organisationService, EventService eventService) {
55+
public ChatController(
56+
JwtUtil jwtUtil,
57+
AngelosService angelosService,
58+
StudyProgramService studyProgramService,
59+
OrganisationService organisationService,
60+
EventService eventService,
61+
QaLogService qaLogService
62+
) {
5563
this.jwtUtil = jwtUtil;
5664
this.angelosService = angelosService;
5765
this.studyProgramService = studyProgramService;
5866
this.organisationService = organisationService;
5967
this.eventService = eventService;
68+
this.qaLogService = qaLogService;
6069
}
6170

6271
/**
@@ -91,6 +100,14 @@ public ResponseEntity<AngelosChatResponse> chat(@RequestHeader("x-api-key") Stri
91100

92101
eventService.logEventAsync("chat_request_completed", null, orgId);
93102

103+
// Log question and answer (temporary)
104+
qaLogService.logAsync(
105+
lastMessage.getMessage(),
106+
response.getAnswer(),
107+
request.getStudy_program(),
108+
orgId
109+
);
110+
94111
return ResponseEntity.ok(response);
95112
} catch (Exception e) {
96113
eventService.logEventAsync("chat_response_failed", "{\"error\": \"" + e.getMessage() + "\"}", orgId);

application-server/src/main/java/com/ase/angelos_kb_backend/controller/EventController.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515
import com.ase.angelos_kb_backend.dto.EventLogDTO;
1616
import com.ase.angelos_kb_backend.dto.EventRequestDTO;
1717
import com.ase.angelos_kb_backend.dto.LimitDTO;
18+
import com.ase.angelos_kb_backend.dto.QaLogDTO;
1819
import com.ase.angelos_kb_backend.dto.TimeframeRequestDTO;
20+
import com.ase.angelos_kb_backend.dto.UserDetailsDTO;
1921
import com.ase.angelos_kb_backend.model.EventLog;
2022
import com.ase.angelos_kb_backend.service.AngelosService;
2123
import com.ase.angelos_kb_backend.service.EunomiaService;
2224
import com.ase.angelos_kb_backend.service.EventService;
25+
import com.ase.angelos_kb_backend.service.QaLogService;
26+
import com.ase.angelos_kb_backend.service.UserService;
2327
import com.ase.angelos_kb_backend.util.JwtUtil;
2428

2529
@RestController
@@ -30,6 +34,8 @@ public class EventController {
3034
private final EunomiaService eunomiaService;
3135
private final AngelosService angelosService;
3236
private final JwtUtil jwtUtil;
37+
private final QaLogService qaLogService;
38+
private final UserService userService;
3339

3440
@Value("${angelos.username}")
3541
private String angelosUsername;
@@ -46,11 +52,13 @@ public class EventController {
4652
@Value("${app.mail.limit}")
4753
private int mailLimit;
4854

49-
public EventController(EventService eventService, EunomiaService eunomiaService, AngelosService angelosService, JwtUtil jwtUtil) {
55+
public EventController(EventService eventService, EunomiaService eunomiaService, AngelosService angelosService, JwtUtil jwtUtil, QaLogService qaLogService, UserService userService) {
5056
this.eventService = eventService;
5157
this.eunomiaService = eunomiaService;
5258
this.angelosService = angelosService;
5359
this.jwtUtil = jwtUtil;
60+
this.qaLogService = qaLogService;
61+
this.userService = userService;
5462
}
5563

5664
@PostMapping("/create")
@@ -81,4 +89,21 @@ public ResponseEntity<LimitDTO> getLimits() {
8189
LimitDTO dto = new LimitDTO(totalLimit, chatLimit, mailLimit);
8290
return ResponseEntity.ok(dto);
8391
}
92+
93+
@GetMapping("/qa-logs")
94+
public ResponseEntity<List<QaLogDTO>> getQaLogs(@RequestHeader("Authorization") String token) {
95+
String email = jwtUtil.extractEmail(token.replace("Bearer ", ""));
96+
UserDetailsDTO user = userService.findMe(email);
97+
98+
List<QaLogDTO> result;
99+
if (Boolean.TRUE.equals(user.isSystemAdmin())) {
100+
// System admin → all orgs
101+
result = qaLogService.getAll();
102+
} else {
103+
// Regular user → restrict to their org
104+
Long orgId = jwtUtil.extractOrgId(token.replace("Bearer ", ""));
105+
result = qaLogService.getByOrg(orgId);
106+
}
107+
return ResponseEntity.ok(result);
108+
}
84109
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.ase.angelos_kb_backend.dto;
2+
3+
import java.time.LocalDateTime;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
7+
@Data
8+
@AllArgsConstructor
9+
public class QaLogDTO {
10+
private String id;
11+
private LocalDateTime createdAt;
12+
private String question;
13+
private String answer;
14+
private String studyProgram;
15+
private Long orgId;
16+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.ase.angelos_kb_backend.model;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Data;
5+
6+
import java.time.LocalDateTime;
7+
import java.util.UUID;
8+
9+
@Entity
10+
@Data
11+
@Table(
12+
name = "qa_logs",
13+
indexes = {
14+
@Index(name = "idx_qalogs_created_at", columnList = "createdAt")
15+
}
16+
)
17+
public class QaLog {
18+
@Id
19+
@GeneratedValue(strategy = GenerationType.UUID)
20+
private UUID id;
21+
22+
@Column(columnDefinition = "TEXT", nullable = false)
23+
private String question;
24+
25+
@Column(columnDefinition = "TEXT", nullable = false)
26+
private String answer;
27+
28+
@Column(length = 255)
29+
private String studyProgram;
30+
31+
private Long orgId;
32+
33+
private LocalDateTime createdAt;
34+
35+
@PrePersist
36+
protected void onCreate() {
37+
createdAt = LocalDateTime.now();
38+
}
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.ase.angelos_kb_backend.repository;
2+
3+
import com.ase.angelos_kb_backend.model.QaLog;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.time.LocalDateTime;
7+
import java.util.List;
8+
import java.util.UUID;
9+
10+
public interface QaLogRepository extends JpaRepository<QaLog, UUID> {
11+
List<QaLog> findAllByOrderByCreatedAtDesc();
12+
List<QaLog> findByOrgIdOrderByCreatedAtDesc(Long orgId);
13+
14+
long deleteByCreatedAtBefore(LocalDateTime cutoff);
15+
}

application-server/src/main/java/com/ase/angelos_kb_backend/service/CleanupService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
package com.ase.angelos_kb_backend.service;
22

33
import java.time.Instant;
4+
import java.time.LocalDateTime;
45
import java.time.temporal.ChronoUnit;
56

67
import org.springframework.scheduling.annotation.Scheduled;
78
import org.springframework.stereotype.Service;
89
import org.springframework.transaction.annotation.Transactional;
910

1011
import com.ase.angelos_kb_backend.repository.EventRepository;
12+
import com.ase.angelos_kb_backend.repository.QaLogRepository;
1113

1214

1315
@Service
1416
public class CleanupService {
1517
private final EventRepository eventLogRepository;
18+
private final QaLogRepository qaLogRepository;
1619

17-
public CleanupService(EventRepository eventLogRepository) {
20+
public CleanupService(EventRepository eventLogRepository, QaLogRepository qaLogRepository) {
1821
this.eventLogRepository = eventLogRepository;
22+
this.qaLogRepository = qaLogRepository;
1923
}
2024

2125
@Scheduled(cron = "0 15 2 * * ?")
@@ -27,4 +31,12 @@ public void cleanOldEventLogs() {
2731
long countAfter = eventLogRepository.count();
2832
System.out.println("Deleted " + (countBefore - countAfter) + " old event logs.");
2933
}
34+
35+
@Scheduled(cron = "0 10 3 * * ?")
36+
@Transactional
37+
public void cleanOldQaLogs() {
38+
LocalDateTime cutoff = LocalDateTime.now().minus(30, ChronoUnit.DAYS);
39+
long deleted = qaLogRepository.deleteByCreatedAtBefore(cutoff);
40+
System.out.println("Deleted " + deleted + " old QA logs (older than 30 days).");
41+
}
3042
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.ase.angelos_kb_backend.service;
2+
3+
import com.ase.angelos_kb_backend.dto.QaLogDTO;
4+
import com.ase.angelos_kb_backend.model.QaLog;
5+
import com.ase.angelos_kb_backend.repository.QaLogRepository;
6+
7+
import java.util.List;
8+
import java.util.stream.Collectors;
9+
10+
import org.springframework.scheduling.annotation.Async;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
@Service
15+
public class QaLogService {
16+
17+
private final QaLogRepository qaLogRepository;
18+
19+
public QaLogService(QaLogRepository repo) {
20+
this.qaLogRepository = repo;
21+
}
22+
23+
@Transactional
24+
public void log(String question, String answer, String studyProgram, Long orgId) {
25+
QaLog log = new QaLog();
26+
log.setQuestion(question);
27+
log.setAnswer(answer);
28+
log.setStudyProgram(studyProgram);
29+
log.setOrgId(orgId);
30+
qaLogRepository.save(log);
31+
}
32+
33+
@Async
34+
public void logAsync(String question, String answer, String studyProgram, Long orgId) {
35+
try {
36+
QaLog log = new QaLog();
37+
log.setQuestion(question);
38+
log.setAnswer(answer);
39+
log.setStudyProgram(studyProgram);
40+
log.setOrgId(orgId);
41+
qaLogRepository.save(log);
42+
} catch (Exception e) {
43+
System.err.println("QA log failed: " + e.getMessage());
44+
}
45+
}
46+
47+
public List<QaLogDTO> getAll() {
48+
return qaLogRepository.findAllByOrderByCreatedAtDesc()
49+
.stream().map(this::toDto).collect(Collectors.toList());
50+
}
51+
52+
public List<QaLogDTO> getByOrg(Long orgId) {
53+
return qaLogRepository.findByOrgIdOrderByCreatedAtDesc(orgId)
54+
.stream().map(this::toDto).collect(Collectors.toList());
55+
}
56+
57+
private QaLogDTO toDto(QaLog q) {
58+
return new QaLogDTO(
59+
q.getId().toString(),
60+
q.getCreatedAt(),
61+
q.getQuestion(),
62+
q.getAnswer(),
63+
q.getStudyProgram(),
64+
q.getOrgId()
65+
);
66+
}
67+
}

knowledge-manager-ui/src/app/components/dashboard/dashboard.component.css

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
background-color: white;
5454
}
5555

56-
.statcontent {
56+
.stat-content {
5757
display: flex;
5858
align-items: center;
5959
gap: 16px;
@@ -144,4 +144,15 @@
144144
font-weight: 700;
145145
color: var(--primary-color);
146146
margin: 24px 0 16px 0;
147+
}
148+
149+
.log-toggle {
150+
margin-bottom: 8px;
151+
}
152+
153+
.empty-msg {
154+
text-align: center;
155+
margin: 24px 0;
156+
color: #666;
157+
font-style: italic;
147158
}

knowledge-manager-ui/src/app/components/dashboard/dashboard.component.html

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ <h1 class="page-header">Dashboard</h1>
2727

2828
<div class="stats">
2929
<mat-card class="stat-box">
30-
<div class="statcontent">
30+
<div class="stat-content">
3131
<mat-icon class="stat-icon">chat</mat-icon>
3232
<div>
3333
<h3>{{ chatCount }}</h3>
@@ -37,14 +37,14 @@ <h3>{{ chatCount }}</h3>
3737
</mat-card>
3838

3939
<mat-card class="stat-box">
40-
<div class="statcontent">
40+
<div class="stat-content">
4141
<mat-icon class="stat-icon">mail</mat-icon>
4242
<div>
4343
<h3>{{ mailSensitive }}</h3>
4444
<p class="stat-label">Sensitive E-Mails</p>
4545
</div>
4646
</div>
47-
<div class="statcontent">
47+
<div class="stat-content">
4848
<mat-icon class="stat-icon">alternate_email</mat-icon>
4949
<div>
5050
<h3>{{ mailAuto }}</h3>
@@ -74,11 +74,37 @@ <h3 class="section-header">Aktuelle Limits</h3>
7474
<mat-card>{{ limits.mailLimit }} Mail-Antworten/Tag (Account)</mat-card>
7575
</div>
7676

77-
<!-- ---------- FEEDBACK LOG -------------------------- -->
78-
<h3 class="section-header">Feedback Log</h3>
79-
<app-main-table
77+
<!-- ---------- FEEDBACK / Q&A LOG -------------------------- -->
78+
<h3 class="section-header">Logs</h3>
79+
80+
<mat-button-toggle-group
81+
class="log-toggle"
82+
[value]="logMode"
83+
(change)="onLogModeChange($event.value)">
84+
<mat-button-toggle value="feedback">
85+
Feedback Log
86+
</mat-button-toggle>
87+
<mat-button-toggle value="qa">
88+
Fragen & Antworten
89+
</mat-button-toggle>
90+
</mat-button-toggle-group>
91+
92+
<!-- Feedback table -->
93+
<app-main-table *ngIf="logMode==='feedback'"
8094
[dataSource]="feedbackRows"
8195
[columns]="feedbackColumns"
8296
[parentComponent]="this">
8397
</app-main-table>
98+
99+
<!-- Q&A table + loading -->
100+
<div *ngIf="logMode==='qa'">
101+
<p *ngIf="qaLoading" class="empty-msg">Daten werden geladen...</p>
102+
<app-main-table *ngIf="!qaLoading"
103+
[dataSource]="qaRows"
104+
[columns]="qaColumns"
105+
[parentComponent]="this">
106+
</app-main-table>
107+
</div>
108+
109+
<div class="spacer" style="height: 12px;"></div>
84110
</div>

0 commit comments

Comments
 (0)