Skip to content

Commit

Permalink
Merge pull request #106 from rwth-acis/user-dashboard
Browse files Browse the repository at this point in the history
Add implementation for a user dashboard
  • Loading branch information
istvank authored May 17, 2021
2 parents 6d10c20 + c654203 commit 5b0a9eb
Show file tree
Hide file tree
Showing 17 changed files with 366 additions and 43 deletions.
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ the [GitHub Release Page](https://github.com/rwth-acis/RequirementsBazaar/releas
- Categories, projects and requirements now have a `userContext` encapsuling the dynamic user related information (
permissions, votes, contribution) [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94).
- If a user is a member of a project the respective role is now returned in the`usertRole` attribute of the
new `userContext` attribute [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96).
new `userContext`
attribute [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96)
.
- Add a delete projects endpoint [#100](https://github.com/rwth-acis/RequirementsBazaar/pull/100).
- Add an update comment endpoint [#100](https://github.com/rwth-acis/RequirementsBazaar/pull/100).
- Redacted comments now have a deleted flag [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).
- Added a user dashboard listing the last 10 most recent active followed projects, categories and
requirements [#106](https://github.com/rwth-acis/RequirementsBazaar/pull/106).

### Changed

Expand Down Expand Up @@ -54,9 +59,14 @@ the [GitHub Release Page](https://github.com/rwth-acis/RequirementsBazaar/releas
- Requirements no longer return the category objects in the `categories` attribute but a list of category
ids [#91](https://github.com/rwth-acis/RequirementsBazaar/pull/91).
- Vote direction can no longer be provided as a query
parameter [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) but instead as a direction object strictly defined by an enum [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96),
parameter [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94) but instead as a direction object strictly
defined by an enum [#96](https://github.com/rwth-acis/RequirementsBazaar/pull/96),
- Moved user related information in categories, requirements and projects (isFollower/Developer/Contributor, userVoted)
into the new `userContext` [#94](https://github.com/rwth-acis/RequirementsBazaar/pull/94).
- Comments with existing responses will no longer be deleted but
redacted [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).
- Comments for a requirement are no longer paginated but instead return all
comments [#103](https://github.com/rwth-acis/RequirementsBazaar/pull/103).

### Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ public interface DALFacade {

/**
* Deletes a given project
*
* @param projectId id of the project to delete
* @param userId id of the user
* @param userId id of the user
*/
Project deleteProjectById(int projectId, Integer userId) throws Exception;

Expand Down Expand Up @@ -226,6 +227,16 @@ public interface DALFacade {
* @throws BazaarException
*/
void removeUserFromProject(int userId, Integer context) throws BazaarException;

/**
* Returns the count most recent active projects followed by the user
*
* @param userId id of the follower
* @param count how many should be returned
* @return Followed projects ordered by last activity
*/
List<Project> getFollowedProjects(int userId, int count) throws BazaarException;

//endregion

//region ProjectFollower
Expand Down Expand Up @@ -364,6 +375,15 @@ public interface DALFacade {
boolean isRequirementPublic(int requirementId) throws BazaarException;

Statistic getStatisticsForRequirement(int userId, int requirementId, Calendar timestamp) throws BazaarException;

/**
* Returns the count most recent active requirements followed by the user
*
* @param userId id of the follower
* @param count how many should be returned
* @return Followed requirements ordered by last activity
*/
List<Requirement> getFollowedRequirements(int userId, int count) throws BazaarException;
//endregion

//region Category
Expand Down Expand Up @@ -417,6 +437,15 @@ public interface DALFacade {
boolean isCategoryPublic(int categoryId) throws BazaarException;

Statistic getStatisticsForCategory(int userId, int categoryId, Calendar timestamp) throws BazaarException;

/**
* Returns the count most recent active categories followed by the user
*
* @param userId id of the follower
* @param count how many should be returned
* @return Followed categories ordered by last activity
*/
List<Category> getFollowedCategories(int userId, int count) throws BazaarException;
//endregion

//region CategoryFollower
Expand Down Expand Up @@ -508,6 +537,7 @@ public interface DALFacade {

/**
* Updates a comment
*
* @param comment comment to persist
* @return the updated comment
* @throws Exception
Expand Down Expand Up @@ -681,4 +711,14 @@ public interface DALFacade {
Feedback getFeedbackById(int feedbackId) throws Exception;

// endregion feedback

/**
* Aggregates the data for the dashboard
*
* @param userId Id of the user for their individual dashboard
* @param count Number of items per group
* @return
* @throws BazaarException
*/
Dashboard getDashboardData(int userId, int count) throws BazaarException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,13 @@ public PaginationResult<User> listFollowersForProject(int projectId, Pageable pa
return userRepository.findAllByFollowing(projectId, 0, 0, pageable);
}

@Override
public List<Project> getFollowedProjects(int userId, int count) throws BazaarException {
projectRepository = (projectRepository != null) ? projectRepository : new ProjectRepositoryImpl(dslContext);
return projectRepository.getFollowedProjects(userId, count);
}


@Override
public List<Requirement> listRequirements(Pageable pageable) throws BazaarException {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
Expand Down Expand Up @@ -395,6 +402,12 @@ public Statistic getStatisticsForRequirement(int userId, int requirementId, Cale
return requirementRepository.getStatisticsForRequirement(userId, requirementId, timestamp.toLocalDateTime());
}

@Override
public List<Requirement> getFollowedRequirements(int userId, int count) throws BazaarException {
requirementRepository = (requirementRepository != null) ? requirementRepository : new RequirementRepositoryImpl(dslContext);
return requirementRepository.getFollowedRequirements(userId, count);
}

@Override
public PaginationResult<Category> listCategoriesByProjectId(int projectId, Pageable pageable, int userId) throws BazaarException {
categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
Expand Down Expand Up @@ -462,6 +475,12 @@ public Statistic getStatisticsForCategory(int userId, int categoryId, Calendar s
return categoryRepository.getStatisticsForCategory(userId, categoryId, timestamp.toLocalDateTime());
}

@Override
public List<Category> getFollowedCategories(int userId, int count) throws BazaarException {
categoryRepository = (categoryRepository != null) ? categoryRepository : new CategoryRepositoryImpl(dslContext);
return categoryRepository.getFollowedCategories(userId, count);
}

@Override
public PaginationResult<User> listFollowersForCategory(int categoryId, Pageable pageable) throws BazaarException {
userRepository = (userRepository != null) ? userRepository : new UserRepositoryImpl(dslContext);
Expand Down Expand Up @@ -541,7 +560,7 @@ public Comment deleteCommentById(int commentId) throws Exception {
comment.setDeleted(true);
comment.setMessage("[This message has been deleted]");
commentRepository.update(comment);
} else{
} else {
commentRepository.delete(commentId);
}
return comment;
Expand Down Expand Up @@ -742,6 +761,15 @@ public Feedback getFeedbackById(int feedbackId) throws Exception {
return feedbackRepository.findById(feedbackId);
}

@Override
public Dashboard getDashboardData(int userId, int count) throws BazaarException {
return Dashboard.builder()
.projects(getFollowedProjects(userId, count))
.categories(getFollowedCategories(userId, count))
.requirements(getFollowedRequirements(userId, count))
.build();
}

@Override
public PaginationResult<ProjectMember> getProjectMembers(int projectId, Pageable pageable) throws BazaarException {
roleRepository = (roleRepository != null) ? roleRepository : new RoleRepositoryImpl(dslContext);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package de.rwth.dbis.acis.bazaar.service.dal.entities;

import com.fasterxml.jackson.annotation.JsonIgnore;
import de.rwth.dbis.acis.bazaar.service.dal.helpers.UserVote;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.jackson.Jacksonized;

import javax.validation.constraints.NotNull;
import java.util.List;

@EqualsAndHashCode(callSuper = true)
@Data
@Jacksonized
@Builder(builderClassName = "Builder")
public class Dashboard extends EntityBase {

@NotNull
private List<Project> projects;

@NotNull
private List<Category> categories;

@NotNull
private List<Requirement> requirements;

@JsonIgnore
@Override
public int getId() {
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class User extends EntityBase {

@NotNull(message = "las2peerId can't be null")
@Size(min = 1, max = 1000, message = "las2peerId must have between 1 and 1000 characters")
@JsonView(SerializerViews.Private.class)
private String las2peerId;

private String profileImage;
Expand Down Expand Up @@ -107,11 +108,15 @@ public Boolean isPersonalizationEnabled() {

@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof User)) return false;
if (o == this) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User other = (User) o;

return this.las2peerId.equals(other.las2peerId);
return las2peerId.equals(other.las2peerId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class PageInfo implements Pageable {
private final List<SortField> sorts;
private final String search;
private final List<Integer> ids;
private final List <String> embed;
private final List<String> embed;

public PageInfo(int pageNumber, int pageSize) {
this(pageNumber, pageSize, new HashMap<>(), new ArrayList<>(), null, null);
Expand All @@ -50,15 +50,21 @@ public PageInfo(int pageNumber, int pageSize) {
public PageInfo(int pageNumber, int pageSize, Map<String, String> filters) {
this(pageNumber, pageSize, filters, new ArrayList<>(), null);
}
public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search){
this(pageNumber, pageSize, filters, sorts, search,null);

public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts) {
this(pageNumber, pageSize, filters, sorts, null);
}
public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search, List<Integer> ids){
this(pageNumber, pageSize, filters, sorts, search,ids, null);

public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search) {
this(pageNumber, pageSize, filters, sorts, search, null);
}

public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search, List<Integer> ids) {
this(pageNumber, pageSize, filters, sorts, search, ids, null);
}

public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search, List<Integer> ids, List <String> embed) {

public PageInfo(int pageNumber, int pageSize, Map<String, String> filters, List<SortField> sorts, String search, List<Integer> ids, List<String> embed) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.filters = filters;
Expand Down Expand Up @@ -104,5 +110,7 @@ public List<String> getEmbed() {
}

@Override
public String getSearch() {return search;}
public String getSearch() {
return search;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ public interface CategoryRepository extends Repository<Category> {
boolean belongsToPublicProject(int id) throws BazaarException;

Statistic getStatisticsForCategory(int userId, int categoryId, LocalDateTime timestamp) throws BazaarException;

List<Category> getFollowedCategories(int userId, int count) throws BazaarException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -354,4 +354,30 @@ public Statistic getStatisticsForCategory(int userId, int categoryId, LocalDateT
}
return result;
}

@Override
public List<Category> getFollowedCategories(int userId, int count) throws BazaarException {
List<Category> categories = null;
try {
List<Integer> categoryIds;
categoryIds = jooq.select()
.from(CATEGORY_FOLLOWER_MAP)
.where(CATEGORY_FOLLOWER_MAP.USER_ID.eq(userId))
.fetch(CATEGORY_FOLLOWER_MAP.CATEGORY_ID);

Condition filterCondition = transformer.getTableId().in(categoryIds);

Pageable.SortField sortField = new Pageable.SortField("last_activity", "DESC");
List<Pageable.SortField> sortList = new ArrayList<>();
sortList.add(sortField);

PageInfo filterPage = new PageInfo(0, count, new HashMap<>(), sortList);

categories = getFilteredCategories(filterCondition, filterPage, userId).left;

} catch (Exception e) {
ExceptionHandler.getInstance().convertAndThrowException(e, ExceptionLocation.REPOSITORY, ErrorCode.UNKNOWN);
}
return categories;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,5 @@ public interface ProjectRepository extends Repository<Project> {

List<Integer> listAllProjectIds(Pageable pageable, int userId) throws BazaarException;



List<Project> getFollowedProjects(int userId, int count) throws BazaarException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,11 @@ public List<Integer> listAllProjectIds(Pageable pageable, int userId) throws Baz
projectIds = jooq.select()
.from(PROJECT)
.where(transformer.getFilterConditions(pageable.getFilters()))
.and(PROJECT.VISIBILITY.isTrue().or(PROJECT.LEADER_ID.equal(userId))
.and(PROJECT.VISIBILITY.isTrue().or(PROJECT.LEADER_ID.equal(userId))
.and(transformer.getSearchCondition(pageable.getSearch())))
.orderBy(transformer.getSortFields(pageable.getSorts()))
// .limit(pageable.getPageSize())
// .offset(pageable.getOffset())
// .limit(pageable.getPageSize())
// .offset(pageable.getOffset())
.fetch(PROJECT.ID);

} catch (Exception e) {
Expand All @@ -267,6 +267,32 @@ public List<Integer> listAllProjectIds(Pageable pageable, int userId) throws Baz
return projectIds;
}

@Override
public List<Project> getFollowedProjects(int userId, int count) throws BazaarException {
List<Project> projects = null;
try {
List<Integer> projectIds;
projectIds = jooq.select()
.from(PROJECT_FOLLOWER_MAP)
.where(PROJECT_FOLLOWER_MAP.USER_ID.eq(userId))
.fetch(PROJECT_FOLLOWER_MAP.PROJECT_ID);

Condition filterCondition = transformer.getTableId().in(projectIds);

Pageable.SortField sortField = new Pageable.SortField("last_activity", "DESC");
List<Pageable.SortField> sortList = new ArrayList<>();
sortList.add(sortField);

PageInfo filterPage = new PageInfo(0, count, new HashMap<>(), sortList);

projects = getFilteredProjects(filterCondition, filterPage, userId).left;

} catch (Exception e) {
ExceptionHandler.getInstance().convertAndThrowException(e, ExceptionLocation.REPOSITORY, ErrorCode.UNKNOWN);
}
return projects;
}


@Override
public boolean belongsToPublicProject(int id) throws BazaarException {
Expand Down Expand Up @@ -342,7 +368,8 @@ public Statistic getStatisticsForVisibleProjects(int userId, LocalDateTime times
}

@Override
public Statistic getStatisticsForProject(int userId, int projectId, LocalDateTime timestamp) throws BazaarException {
public Statistic getStatisticsForProject(int userId, int projectId, LocalDateTime timestamp) throws
BazaarException {
Statistic result = null;
try {
// If you want to change something here, please know what you are doing! Its SQL and even worse JOOQ :-|
Expand Down
Loading

0 comments on commit 5b0a9eb

Please sign in to comment.