Skip to content

Commit

Permalink
Communication: Allow user to save messages for later (#9705)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaRangger authored Nov 26, 2024
1 parent 34d9914 commit 0851344
Show file tree
Hide file tree
Showing 89 changed files with 3,145 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.SQLRestriction;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import de.tum.cit.aet.artemis.communication.domain.conversation.Conversation;
import de.tum.cit.aet.artemis.core.domain.Course;

/**
Expand All @@ -35,10 +39,20 @@ public class AnswerPost extends Posting {
@OneToMany(mappedBy = "answerPost", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<Reaction> reactions = new HashSet<>();

/***
* The value 1 represents an answer post, given by the enum {{@link PostingType}}
*/
@OneToMany(mappedBy = "postId", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
@SQLRestriction("post_type = 1")
private Set<SavedPost> savedPosts = new HashSet<>();

@ManyToOne
@JsonIncludeProperties({ "id", "exercise", "lecture", "course", "courseWideContext", "conversation", "author" })
private Post post;

@Transient
private boolean isSaved = false;

@JsonProperty("resolvesPost")
public Boolean doesResolvePost() {
return resolvesPost;
Expand Down Expand Up @@ -76,6 +90,25 @@ public void setPost(Post post) {
this.post = post;
}

@JsonIgnore
public Set<SavedPost> getSavedPosts() {
return savedPosts;
}

@JsonProperty("isSaved")
public boolean getIsSaved() {
return isSaved;
}

public void setIsSaved(boolean isSaved) {
this.isSaved = isSaved;
}

@JsonIgnore
public Conversation getConversation() {
return getPost().getConversation();
}

/**
* Helper method to extract the course an AnswerPost belongs to, which is found in different locations based on the parent Post's context
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.Size;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.SQLRestriction;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import de.tum.cit.aet.artemis.communication.domain.conversation.Conversation;
import de.tum.cit.aet.artemis.core.domain.Course;
Expand Down Expand Up @@ -54,6 +57,13 @@ public class Post extends Posting {
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<AnswerPost> answers = new HashSet<>();

/***
* The value 0 represents a post, given by the enum {{@link PostingType}}
*/
@OneToMany(mappedBy = "postId", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
@SQLRestriction("post_type = 0")
private Set<SavedPost> savedPosts = new HashSet<>();

@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "post_tag", joinColumns = @JoinColumn(name = "post_id"))
@Column(name = "text")
Expand Down Expand Up @@ -96,6 +106,9 @@ public class Post extends Posting {
@Column(name = "vote_count")
private int voteCount;

@Transient
private boolean isSaved = false;

public Post() {
}

Expand Down Expand Up @@ -222,6 +235,20 @@ public void setVoteCount(Integer voteCount) {
this.voteCount = voteCount != null ? voteCount : 0;
}

@JsonIgnore
public Set<SavedPost> getSavedPosts() {
return savedPosts;
}

@JsonProperty("isSaved")
public boolean getIsSaved() {
return isSaved;
}

public void setIsSaved(boolean isSaved) {
this.isSaved = isSaved;
}

/**
* Helper method to extract the course a Post belongs to, which is found in different locations based on the Post's context
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import de.tum.cit.aet.artemis.communication.domain.conversation.Conversation;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.core.domain.User;
Expand Down Expand Up @@ -118,4 +119,6 @@ public void setAuthorRole(UserRole authorRole) {

@Transient
public abstract Course getCoursePostingBelongsTo();

public abstract Conversation getConversation();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.tum.cit.aet.artemis.communication.domain;

import java.util.Arrays;

public enum PostingType {

POST((short) 0), ANSWER((short) 1);

private final short databaseKey;

PostingType(short databaseKey) {
this.databaseKey = databaseKey;
}

public short getDatabaseKey() {
return databaseKey;
}

public static PostingType fromDatabaseKey(short databaseKey) {
return Arrays.stream(PostingType.values()).filter(type -> type.getDatabaseKey() == databaseKey).findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown database key: " + databaseKey));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package de.tum.cit.aet.artemis.communication.domain;

import java.time.ZonedDateTime;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.core.domain.User;

@Entity
@Table(name = "saved_post")
public class SavedPost extends DomainObject {

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;

@Column(name = "post_id", nullable = false)
private Long postId;

@Enumerated
@Column(name = "post_type", nullable = false)
private PostingType postType;

@Enumerated
@Column(name = "status", nullable = false)
private SavedPostStatus status;

@Column(name = "completed_at")
private ZonedDateTime completedAt;

public SavedPost() {
}

public SavedPost(User user, Long postId, PostingType postType, SavedPostStatus status, ZonedDateTime completedAt) {
this.user = user;
this.postId = postId;
this.postType = postType;
this.status = status;
this.completedAt = completedAt;
}

public Long getPostId() {
return postId;
}

public void setPostId(Long postId) {
this.postId = postId;
}

public void setStatus(SavedPostStatus status) {
this.status = status;
}

public User getUser() {
return user;
}

public SavedPostStatus getStatus() {
return status;
}

public void setCompletedAt(ZonedDateTime completedAt) {
this.completedAt = completedAt;
}

public void setPostType(PostingType postType) {
this.postType = postType;
}

public PostingType getPostType() {
return postType;
}

public ZonedDateTime getCompletedAt() {
return completedAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.tum.cit.aet.artemis.communication.domain;

import java.util.Arrays;

public enum SavedPostStatus {

IN_PROGRESS((short) 0), COMPLETED((short) 1), ARCHIVED((short) 2);

private final short databaseKey;

SavedPostStatus(short databaseKey) {
this.databaseKey = databaseKey;
}

public short getDatabaseKey() {
return databaseKey;
}

public static SavedPostStatus fromDatabaseKey(short databaseKey) {
return Arrays.stream(SavedPostStatus.values()).filter(type -> type.getDatabaseKey() == databaseKey).findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown database key: " + databaseKey));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.tum.cit.aet.artemis.communication.dto;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.core.domain.User;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record AuthorDTO(Long id, String name, String imageUrl) {

public AuthorDTO(User user) {
this(user.getId(), user.getName(), user.getImageUrl());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package de.tum.cit.aet.artemis.communication.dto;

import de.tum.cit.aet.artemis.communication.domain.ConversationType;
import de.tum.cit.aet.artemis.communication.domain.conversation.Channel;
import de.tum.cit.aet.artemis.communication.domain.conversation.Conversation;
import de.tum.cit.aet.artemis.communication.domain.conversation.GroupChat;

public record PostingConversationDTO(Long id, String title, ConversationType type) {

public PostingConversationDTO(Conversation conversation) {
this(conversation.getId(), determineTitle(conversation), determineType(conversation));
}

private static String determineTitle(Conversation conversation) {
if (conversation instanceof Channel) {
return ((Channel) conversation).getName();
}
else if (conversation instanceof GroupChat) {
return ((GroupChat) conversation).getName();
}
else {
return "Chat";
}
}

private static ConversationType determineType(Conversation conversation) {
if (conversation instanceof Channel) {
return ConversationType.CHANNEL;
}
else {
return ConversationType.DIRECT;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.tum.cit.aet.artemis.communication.dto;

import java.time.ZonedDateTime;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.communication.domain.AnswerPost;
import de.tum.cit.aet.artemis.communication.domain.Posting;
import de.tum.cit.aet.artemis.communication.domain.PostingType;
import de.tum.cit.aet.artemis.communication.domain.UserRole;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record PostingDTO(Long id, AuthorDTO author, UserRole role, ZonedDateTime creationDate, ZonedDateTime updatedDate, String content, boolean isSaved, short savedPostStatus,
List<ReactionDTO> reactions, PostingConversationDTO conversation, short postingType, Long referencePostId) {

public PostingDTO(Posting post, boolean isSaved, short savedPostStatus) {
this(post.getId(), new AuthorDTO(post.getAuthor()), post.getAuthorRole(), post.getCreationDate(), post.getUpdatedDate(), post.getContent(), isSaved, savedPostStatus,
post.getReactions().stream().map(ReactionDTO::new).toList(), new PostingConversationDTO(post.getConversation()), getSavedPostType(post).getDatabaseKey(),
getReferencePostId(post));
}

static PostingType getSavedPostType(Posting posting) {
if (posting instanceof AnswerPost) {
return PostingType.ANSWER;
}
else {
return PostingType.POST;
}
}

static Long getReferencePostId(Posting posting) {
if (posting instanceof AnswerPost) {
return ((AnswerPost) posting).getPost().getId();
}
else {
return posting.getId();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.tum.cit.aet.artemis.communication.dto;

import java.time.ZonedDateTime;

import de.tum.cit.aet.artemis.communication.domain.Reaction;

public record ReactionDTO(Long id, AuthorDTO user, ZonedDateTime creationDate, String emojiId) {

public ReactionDTO(Reaction reaction) {
this(reaction.getId(), new AuthorDTO(reaction.getUser()), reaction.getCreationDate(), reaction.getEmojiId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,12 @@ default AnswerPost findAnswerMessageByIdElseThrow(Long answerPostId) {
return getValueElseThrow(findById(answerPostId).filter(answerPost -> answerPost.getPost().getConversation() != null), answerPostId);
}

@NotNull
default AnswerPost findAnswerPostOrMessageByIdElseThrow(Long answerPostId) {
return getValueElseThrow(findById(answerPostId), answerPostId);
}

long countAnswerPostsByPostIdIn(List<Long> postIds);

List<AnswerPost> findByIdIn(List<Long> idList);
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ private PageImpl<Post> findPostsWithSpecification(Pageable pageable, Specificati
LEFT JOIN FETCH p.conversation
LEFT JOIN FETCH p.reactions
LEFT JOIN FETCH p.tags
LEFT JOIN FETCH p.savedPosts
LEFT JOIN FETCH p.answers a
LEFT JOIN FETCH a.reactions
LEFT JOIN FETCH a.post
Expand Down
Loading

0 comments on commit 0851344

Please sign in to comment.