Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TRIB-174: Ensure user count is included when attribute is returned #152

Merged
merged 15 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.savvato.tribeapp.controllers;

import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.*;
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.ApplyPhraseToUser;
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.DeletePhraseFromUser;
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.GetAttributesForUser;
import com.savvato.tribeapp.controllers.annotations.controllers.AttributesAPIController.GetUserPhrasesToBeReviewed;
import com.savvato.tribeapp.controllers.dto.AttributesRequest;
import com.savvato.tribeapp.dto.AttributeDTO;
import com.savvato.tribeapp.dto.GenericMessageDTO;
import com.savvato.tribeapp.dto.ToBeReviewedDTO;
import com.savvato.tribeapp.entities.NotificationType;
import com.savvato.tribeapp.services.*;
Expand Down Expand Up @@ -43,19 +45,6 @@ public class AttributesAPIController {
AttributesAPIController() {
}

@GetNumberOfUsersWithAttribute
@GetMapping("/total/{attributeId}")
public ResponseEntity<GenericMessageDTO> getNumberOfUsersWithAttribute(
@Parameter(description = "Attribute ID", example = "1") @PathVariable Long attributeId) {

Optional<Integer> numberOfUsers = attributesService.getNumberOfUsersWithAttribute(attributeId);

if (numberOfUsers.isPresent()) {
String userCount = String.valueOf(numberOfUsers.get());
return ResponseEntity.ok(GenericMessageDTO.builder().responseMessage(userCount).build());
}
return ResponseEntity.badRequest().build();
}

@GetAttributesForUser
@GetMapping("/{userId}")
Expand Down

This file was deleted.

4 changes: 3 additions & 1 deletion src/main/java/com/savvato/tribeapp/dto/AttributeDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
@Schema(description = "An attribute DTO")
@Builder
public class AttributeDTO {
public PhraseDTO phrase;
public PhraseDTO phrase;
@Schema(example = "1")
public Integer userCount;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.savvato.tribeapp.dto.projections;


public record PhraseWithUserCountDTO(Long id, Long adverbId, Long verbId, Long prepositionId, Long nounId,
Long userCount) {

}
20 changes: 16 additions & 4 deletions src/main/java/com/savvato/tribeapp/entities/Phrase.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.savvato.tribeapp.entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.List;

@Entity
@AllArgsConstructor

Check warning on line 10 in src/main/java/com/savvato/tribeapp/entities/Phrase.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/savvato/tribeapp/entities/Phrase.java#L10

Added line #L10 was not covered by tests
@NoArgsConstructor
public class Phrase {

@Id
Expand Down Expand Up @@ -60,4 +63,13 @@
public void setNounId(Long nounID) {
this.nounId = nounID;
}

@OneToMany
@JoinTable(
name = "user_phrase",
joinColumns = {@JoinColumn(name = "phraseId")},
inverseJoinColumns = {@JoinColumn(name = "userId")})
private List<User> users;


}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.savvato.tribeapp.repositories;


import com.savvato.tribeapp.dto.projections.PhraseWithUserCountDTO;
import com.savvato.tribeapp.entities.Phrase;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
Expand All @@ -11,8 +12,10 @@
@Repository
public interface PhraseRepository extends CrudRepository<Phrase, Long> {

@Query(nativeQuery = true, value = "select * from phrase where id = ?")
Optional<Phrase> findPhraseByPhraseId(Long Id);
@Query("""
SELECT new com.savvato.tribeapp.dto.projections.PhraseWithUserCountDTO(p.id, p.adverbId, p.verbId, p.prepositionId, p.nounId, COUNT(u.id) AS userCount) FROM Phrase p LEFT JOIN p.users u GROUP BY p HAVING p.id = :id
""")
Optional<PhraseWithUserCountDTO> findPhraseByPhraseId(Long id);

Optional<Phrase> findByAdverbIdAndVerbIdAndPrepositionIdAndNounId(Long AdverbId, Long VerbId, Long PrepositionId, Long NounId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,4 @@
public interface UserPhraseRepository extends CrudRepository<UserPhrase, UserPhraseId> {
@Query(nativeQuery = true, value = "select phrase_id from user_phrase where user_id = ?")
Optional<List<Long>> findPhraseIdsByUserId(Long Id);

@Query(nativeQuery = true, value = "select COUNT(*) FROM user_phrase where attribute_id = ?")
Integer countUsersWithAttribute(Long attributeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@
public interface AttributesService {

Optional<List<AttributeDTO>> getAttributesByUserId(Long id);

Optional<Integer> getNumberOfUsersWithAttribute(Long attributeId);
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
Expand All @@ -22,33 +23,24 @@ public class AttributesServiceImpl implements AttributesService {
@Override
public Optional<List<AttributeDTO>> getAttributesByUserId(Long userId) {

List<AttributeDTO> attributes = new ArrayList<>();

// Get all user phrases as phraseDTOs
Optional<List<PhraseDTO>> optUserPhraseDTOs = phraseService.getListOfPhraseDTOByUserIdWithoutPlaceholderNullvalue(userId);
Optional<Map<PhraseDTO, Integer>> optUserPhraseDTOs = phraseService.getPhraseInformationByUserId(userId);

// If there are phrases, build DTO and add to attributes list
if (optUserPhraseDTOs.isPresent()) {
List<PhraseDTO> phrases = optUserPhraseDTOs.get();
for (PhraseDTO phrase : phrases) {
AttributeDTO attributeDTO = AttributeDTO.builder()
.phrase(phrase)
.build();
attributes.add(attributeDTO);
}
Map<PhraseDTO, Integer> phraseDTOUserCountMap = optUserPhraseDTOs.get();
List<AttributeDTO> attributes = phraseDTOUserCountMap
.entrySet()
.stream()
.map(
entry -> AttributeDTO.builder().phrase(entry.getKey()).userCount(entry.getValue()).build()
)
.toList();
return Optional.of(attributes);
}

// Returns list of attributeDTOs. Can be empty.
return Optional.of(attributes);
}

@Override
public Optional<Integer> getNumberOfUsersWithAttribute(Long attributeId) {
try {
Integer numberOfUsers = userPhraseRepository.countUsersWithAttribute(attributeId);
return Optional.of(numberOfUsers);
} catch (Exception e) {
return Optional.empty();
}
return Optional.of(new ArrayList<>());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.savvato.tribeapp.dto.PhraseDTO;

import java.util.List;
import java.util.Map;
import java.util.Optional;

public interface PhraseService {
Expand All @@ -13,5 +13,5 @@ public interface PhraseService {

Optional<Long> findPreviouslyApprovedPhraseId(String adverb, String verb, String preposition, String noun);

Optional<List<PhraseDTO>> getListOfPhraseDTOByUserIdWithoutPlaceholderNullvalue(Long id);
Optional<Map<PhraseDTO, Integer>> getPhraseInformationByUserId(Long userId);
}
103 changes: 41 additions & 62 deletions src/main/java/com/savvato/tribeapp/services/PhraseServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

import com.savvato.tribeapp.constants.Constants;
import com.savvato.tribeapp.dto.PhraseDTO;
import com.savvato.tribeapp.dto.projections.PhraseWithUserCountDTO;
import com.savvato.tribeapp.entities.*;
import com.savvato.tribeapp.repositories.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import java.util.*;


@Service
Expand Down Expand Up @@ -60,7 +58,7 @@ public boolean isPhraseValid(String adverb, String verb, String preposition, Str
String prepositionLowerCase = changeToLowerCase(preposition);
String nounLowerCase = changeToLowerCase(noun);

if(isMissingVerbOrNoun(verbLowerCase,nounLowerCase) ||
if (isMissingVerbOrNoun(verbLowerCase, nounLowerCase) ||
isAnyWordRejected(adverbLowerCase, verbLowerCase, prepositionLowerCase, nounLowerCase) ||
isPhrasePreviouslyRejected(adverbLowerCase, verbLowerCase, prepositionLowerCase, nounLowerCase)) {
log.warn("Phrase is not valid.");
Expand All @@ -72,7 +70,7 @@ public boolean isPhraseValid(String adverb, String verb, String preposition, Str
}

public String changeToLowerCase(String word) {
if(word != null && !word.trim().isEmpty()){
if (word != null && !word.trim().isEmpty()) {
return word.toLowerCase();
}
return word;
Expand All @@ -91,8 +89,8 @@ public boolean isMissingVerbOrNoun(String verb, String noun) {
public boolean isAnyWordRejected(String adverb, String verb, String preposition, String noun) {

List<String> words = Arrays.asList(adverb, verb, preposition, noun);
for(String word: words) {
if(isWordPreviouslyRejected(word)){
for (String word : words) {
if (isWordPreviouslyRejected(word)) {
log.warn(word + " exists in rejected words.");
return true;
}
Expand All @@ -108,16 +106,20 @@ public boolean isWordPreviouslyRejected(String word) {
public boolean isPhrasePreviouslyRejected(String adverb, String verb, String preposition, String noun) {
StringBuilder rejectedPhraseSB = new StringBuilder();

if(adverb != null && !adverb.trim().isEmpty()) { rejectedPhraseSB.append(adverb + " "); }
if (adverb != null && !adverb.trim().isEmpty()) {
rejectedPhraseSB.append(adverb + " ");
}
rejectedPhraseSB.append(verb + " ");
if(preposition != null && !preposition.trim().isEmpty()) { rejectedPhraseSB.append(preposition + " "); }
if (preposition != null && !preposition.trim().isEmpty()) {
rejectedPhraseSB.append(preposition + " ");
}
rejectedPhraseSB.append(noun);

String rejectedPhraseString = rejectedPhraseSB.toString().trim();

Optional<RejectedPhrase> rejectedPhrase = rejectedPhraseRepository.findByRejectedPhrase(rejectedPhraseString);

if(rejectedPhrase.isPresent()) {
if (rejectedPhrase.isPresent()) {
log.warn(rejectedPhraseString + " exits in rejected phrases.");
return true;
}
Expand Down Expand Up @@ -176,13 +178,13 @@ public Optional<Long> findPreviouslyApprovedPhraseId(String adverb, String verb,
Long prepositionId;
Long nounId;

if(findVerbIfExists(verb).isPresent()) {
if (findVerbIfExists(verb).isPresent()) {
verbId = verbRepository.findByWord(verb).get().getId();
} else {
return Optional.empty();
}

if(findNounIfExists(noun).isPresent()) {
if (findNounIfExists(noun).isPresent()) {
nounId = nounRepository.findByWord(noun).get().getId();
} else {
return Optional.empty();
Expand Down Expand Up @@ -236,66 +238,43 @@ public void addUserAndPhraseToReviewSubmittingUserRepository(Long userId, Long t
}

@Override
public Optional<List<PhraseDTO>> getListOfPhraseDTOByUserIdWithoutPlaceholderNullvalue(Long userId) {

// create list of PhraseDtos
List<PhraseDTO> phraseDTOS = new ArrayList<>();

public Optional<Map<PhraseDTO, Integer>> getPhraseInformationByUserId(Long userId) {
// Create list of phrase ids
Optional<List<Long>> optPhraseIds = userPhraseService.findPhraseIdsByUserId(userId);

// Create list of all phrases from those phrase ids
if (optPhraseIds.isPresent()) {
List<Long> phraseIds = optPhraseIds.get();
List<Phrase> phrases = new ArrayList<>();

// loop through list of phrase ids and add each phrase to list
Map<PhraseDTO, Integer> phraseDTOsWithUserCount = new HashMap<>();
for (Long phraseId : phraseIds) {
Optional<Phrase> optPhrase = phraseRepository.findPhraseByPhraseId(phraseId);
if (optPhrase.isPresent()) {
phrases.add(optPhrase.get());
Optional<PhraseWithUserCountDTO> optPhraseInformation = phraseRepository.findPhraseByPhraseId(phraseId);
if (optPhraseInformation.isPresent()) {
PhraseWithUserCountDTO phraseInformation = optPhraseInformation.get();
PhraseDTO phraseDTO = constructPhraseDTOFromPhraseInformation(phraseInformation.id(), phraseInformation.adverbId(), phraseInformation.verbId(), phraseInformation.prepositionId(), phraseInformation.nounId());
phraseDTOsWithUserCount.put(phraseDTO, phraseInformation.userCount().intValue());
} else {
throw new IllegalStateException("phrase not found");
}
}

// loop through phrases and get words. replace nullvalue placeholders with empty string
for (Phrase phrase : phrases) {
PhraseDTO phraseDTO = PhraseDTO.builder()
.id(phrase.getId())
.build();

Optional<String> optAdverb = adverbRepository.findAdverbById(phrase.getAdverbId());
Optional<String> optVerb = verbRepository.findVerbById(phrase.getVerbId());
Optional<String> optPreposition = prepositionRepository.findPrepositionById(phrase.getPrepositionId());
Optional<String> optNoun = nounRepository.findNounById(phrase.getNounId());

if (optAdverb.isPresent()) {
String adverbText = optAdverb.get();
phraseDTO.adverb = adverbText.equals(Constants.NULL_VALUE_WORD) ? "" : adverbText;
throw new IllegalStateException("message not found");
}
if (optVerb.isPresent()) {
phraseDTO.verb = optVerb.get();
}
if (optPreposition.isPresent()) {
String prepositionText = optPreposition.get();
phraseDTO.preposition = prepositionText.equals(Constants.NULL_VALUE_WORD) ? "" : prepositionText;
}
if (optNoun.isPresent()) {
phraseDTO.noun = optNoun.get();
}

phraseDTOS.add(phraseDTO);
}

return Optional.of(phraseDTOS);

} else {

return Optional.empty();

return Optional.of(phraseDTOsWithUserCount);
}
return Optional.empty();
}

public PhraseDTO constructPhraseDTOFromPhraseInformation(Long phraseId, Long adverbId, Long verbId, Long prepositionId, Long nounId) {
Optional<String> optAdverb = adverbRepository.findAdverbById(adverbId);
Optional<String> optVerb = verbRepository.findVerbById(verbId);
Optional<String> optPreposition = prepositionRepository.findPrepositionById(prepositionId);
Optional<String> optNoun = nounRepository.findNounById(nounId);

return PhraseDTO
.builder()
.id(phraseId)
.adverb(optAdverb.orElse("").replaceFirst(Constants.NULL_VALUE_WORD, ""))
.verb(optVerb.orElse(""))
.preposition(optPreposition.orElse("").replaceFirst(Constants.NULL_VALUE_WORD, ""))
.noun(optNoun.orElse(""))
.build();
}

}
Loading
Loading