Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public class CardController {
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CardResponse[].class)))
schema = @Schema(implementation = CardSummary[].class)))
@GetMapping
public ResponseEntity<List<CardResponse>> getAllCardResponses(
public ResponseEntity<List<CardSummary>> getAllCardResponses(
@RequestParam Long subjectId, @AuthenticationPrincipal Jwt jwt) {
log.info("GET /cards: subjectId={}", subjectId);
User user = currentUserService.getCurrentUser(jwt);
Expand All @@ -71,13 +71,13 @@ public ResponseEntity<List<CardResponse>> getAllCardResponses(
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CardResponse.class)))
schema = @Schema(implementation = CardSummary.class)))
@ApiResponse(
responseCode = "404",
description = "Card not found",
content = @Content(mediaType = "application/json"))
@GetMapping("/{id}")
public ResponseEntity<CardResponse> getById(
public ResponseEntity<CardSummary> getById(
@PathVariable Long id, @AuthenticationPrincipal Jwt jwt) {
currentUserService.getCurrentUser(jwt);
return ResponseEntity.ok(
Expand Down Expand Up @@ -134,8 +134,8 @@ public ResponseEntity<CardResponse> update(
@Valid @RequestBody CardRequest request,
@AuthenticationPrincipal Jwt jwt) {
currentUserService.getCurrentUser(jwt);
CardResponse cardResponse = cardService.updateCard(id, request);
return ResponseEntity.ok(cardResponse);
CardResponse response = cardService.updateCard(id, request);
return ResponseEntity.ok(response);
}

@Operation(summary = "Rate card", description = "Rates a card by its ID.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,85 +1,31 @@
package com.example.flashcards_backend.dto;

import com.example.flashcards_backend.model.Card;
import com.example.flashcards_backend.model.CardHistory;

import java.util.HashSet;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Set;
import java.util.stream.Collectors;

import com.example.flashcards_backend.repository.CardDeckRowProjection;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;

@Builder
public record CardResponse(
@JsonProperty("id") Long id,
@JsonProperty("front") String front,
@JsonProperty("back") String back,
@JsonProperty("hintFront") String hintFront,
@JsonProperty("hintBack") String hintBack,
@JsonProperty("decks") Set<DeckSummary> decks,
@JsonProperty("avgRating") Double avgRating,
@JsonProperty("viewCount") Integer viewCount,
@JsonProperty("lastViewed") String lastViewed,
@JsonProperty("lastRating") Integer lastRating,
@JsonProperty("subjectId") Long subjectId
) {

@JsonCreator
public CardResponse {
// canonical constructor; Jackson will call this
}

public static CardResponse fromEntity(Card card) {
Set<DeckSummary> deckSummaries = card.getDecks().stream()
.map(DeckSummary::fromEntity)
.collect(Collectors.toSet());

CardHistory ch = card.getCardHistories().stream()
.findFirst()
.orElseGet(CardHistory::new);

return new CardResponse(
card.getId(),
card.getFront(),
card.getBack(),
card.getHintFront(),
card.getHintBack(),
deckSummaries,
ch.getAvgRating(),
ch.getViewCount(),
ch.getLastViewed() != null ? ch.getLastViewed().toString() : null,
ch.getLastRating(),
card.getSubject().getId()
);
}

public static CardResponse fromEntity(CardDeckRowProjection cd) {
return new CardResponse(
cd.getCardId(),
cd.getFront(),
cd.getBack(),
cd.getHintFront(),
cd.getHintBack(),
new HashSet<>(),
cd.getAvgRating(),
cd.getViewCount(),
cd.getLastViewed() != null ? cd.getLastViewed().toString() : null,
cd.getLastRating(),
cd.getSubjectId()
);
}

public static CardResponse fromEntity(CreateCardResponse ccr) {
return CardResponse.builder()
.id(ccr.id())
.front(ccr.front())
.back(ccr.back())
.hintFront(ccr.hintFront())
.hintBack(ccr.hintBack())
.decks(new HashSet<>(ccr.decks()))
.build();
}
}
@JsonProperty("id") Long id,
@JsonProperty("front") String front,
@JsonProperty("back") String back,
@JsonProperty("hintFront") String hintFront,
@JsonProperty("hintBack") String hintBack,
@JsonProperty("decks") Set<DeckSummary> decks,
@JsonProperty("subjectId") Long subjectId) {
public static CardResponse fromEntity(Card card) {
Set<DeckSummary> deckSummaries = card.getDecks().stream()
.map(DeckSummary::fromEntity)
.collect(Collectors.toSet());

return new CardResponse(
card.getId(),
card.getFront(),
card.getBack(),
card.getHintFront(),
card.getHintBack(),
deckSummaries,
card.getSubject().getId()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.example.flashcards_backend.dto;

import com.example.flashcards_backend.model.Card;
import com.example.flashcards_backend.model.CardHistory;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import com.example.flashcards_backend.repository.CardDeckRowProjection;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;

@Builder
public record CardSummary(
@JsonProperty("id") Long id,
@JsonProperty("front") String front,
@JsonProperty("back") String back,
@JsonProperty("hintFront") String hintFront,
@JsonProperty("hintBack") String hintBack,
@JsonProperty("decks") Set<DeckSummary> decks,
@JsonProperty("avgRating") Double avgRating,
@JsonProperty("viewCount") Integer viewCount,
@JsonProperty("lastViewed") String lastViewed,
@JsonProperty("lastRating") Integer lastRating,
@JsonProperty("subjectId") Long subjectId) {

@JsonCreator
public CardSummary {
// canonical constructor; Jackson will call this
}

public static CardSummary fromEntity(Card card, CardHistory ch) {
Set<DeckSummary> deckSummaries =
card.getDecks().stream().map(DeckSummary::fromEntity).collect(Collectors.toSet());

if (ch == null) {
ch = CardHistory.builder().avgRating(0.0).viewCount(0).lastViewed(null).lastRating(0).build();
}

return new CardSummary(
card.getId(),
card.getFront(),
card.getBack(),
card.getHintFront(),
card.getHintBack(),
deckSummaries,
ch.getAvgRating(),
ch.getViewCount(),
ch.getLastViewed() != null ? ch.getLastViewed().toString() : null,
ch.getLastRating(),
card.getSubject().getId());
}

public static CardSummary fromEntity(CardDeckRowProjection cd) {
return new CardSummary(
cd.getCardId(),
cd.getFront(),
cd.getBack(),
cd.getHintFront(),
cd.getHintBack(),
new HashSet<>(),
cd.getAvgRating(),
cd.getViewCount(),
cd.getLastViewed() != null ? cd.getLastViewed().toString() : null,
cd.getLastRating(),
cd.getSubjectId());
}

public static CardSummary fromEntity(CreateCardResponse ccr) {
return CardSummary.builder()
.id(ccr.id())
.front(ccr.front())
.back(ccr.back())
.hintFront(ccr.hintFront())
.hintBack(ccr.hintBack())
.decks(new HashSet<>(ccr.decks()))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
import java.util.List;

@Builder
public record CsvUploadResponseDto(List<CardResponse> saved, List<CardResponse> duplicates) {}
public record CsvUploadResponseDto(List<CardSummary> saved, List<CardSummary> duplicates) {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,4 @@
import java.util.UUID;

@Builder
public record UserDto(
String username,
UUID id,
boolean isActive
) {
}
public record UserDto(String username, UUID id, boolean isActive) {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
@Builder
public record UserStatsResponse(
Long totalCards,
CardResponse hardestCard,
CardResponse mostViewedCard,
CardSummary hardestCard,
CardSummary mostViewedCard,
Long totalCardViews,
Long totalLastRating1,
Long totalLastRating2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "CardBuilder", toBuilder = true)
@Builder(toBuilder = true)
@Table(name = "card", uniqueConstraints = @UniqueConstraint(columnNames = {"front", "back"}))
public class Card {
@Id
Expand Down Expand Up @@ -53,12 +53,6 @@ public class Card {
@NotNull
private Subject subject;

@OneToMany(mappedBy = "card", cascade = CascadeType.REMOVE, orphanRemoval = true)
@EqualsAndHashCode.Exclude
@ToString.Exclude
@Singular
private final Set<CardHistory> cardHistories = new HashSet<>();

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;
Expand Down Expand Up @@ -118,44 +112,6 @@ public Set<String> getDeckNames() {
return deckNames;
}

public void addCardHistory(CardHistory cardHistory) {
cardHistory.setCard(this);
}

@SuppressWarnings("unused")
public static class CardBuilder {
private Long id;
private String front;
private String back;
private String hintFront;
private String hintBack;
private Set<Deck> decks = new HashSet<>();
private Set<CardHistory> cardHistories = new HashSet<>();
private Subject subject;
private User user;

public CardBuilder cardHistory(CardHistory cardHistory) {
this.cardHistories.add(cardHistory);
return this;
}

public Card build() {
Card card = new Card();
card.id = this.id;
card.front = this.front;
card.back = this.back;
card.hintFront = this.hintFront;
card.hintBack = this.hintBack;
card.decks = this.decks;
card.subject = this.subject;
card.user = this.user;
for (CardHistory ch : this.cardHistories) {
card.addCardHistory(ch);
}
return card;
}
}

@Override
public final boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,4 @@ public class CardHistory extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;

public void setCard(Card card) {
this.card = card;
if (card != null) {
card.getCardHistories().add(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -38,12 +36,6 @@ public class Subject extends BaseEntity {
@Enumerated(EnumType.STRING)
private CardOrder cardOrder;

@OneToMany(mappedBy = "subject", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Deck> decks = new HashSet<>();

@OneToMany(mappedBy = "subject", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Card> cards = new HashSet<>();

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
@JsonBackReference
Expand Down
Loading