diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java new file mode 100644 index 000000000..9b801cb9b --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/UserStatsController.java @@ -0,0 +1,54 @@ +package com.iqser.red.persistence.service.v1.external.api.impl.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; + +import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService; +import com.iqser.red.service.persistence.management.v1.processor.service.DossierService; +import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService; +import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService; +import com.iqser.red.service.persistence.service.v1.api.external.resource.UserStatsResource; +import com.iqser.red.service.persistence.service.v1.api.shared.model.UserStats; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class UserStatsController implements UserStatsResource { + + private final UserService userService; + private final DossierService dossierService; + private final FileStatusPersistenceService fileStatusPersistenceService; + private final DossierACLService dossierACLService; + + + @Override + public ResponseEntity getUserStats(String userId) { + + if (userService.getUserById(userId).isEmpty()) { + return new ResponseEntity(null, HttpStatus.NOT_FOUND); + } + List dossierMemberships = new ArrayList<>(); + List dossierOwnerships = new ArrayList<>(); + dossierService.getAllDossiers() + .stream() + .forEach(d -> { + if (dossierACLService.getMembers(d.getId()).contains(userId)) { + dossierMemberships.add(d.getId()); + } + if (dossierACLService.getOwners(d.getId()).contains(userId)) { + dossierOwnerships.add(d.getId()); + } + }); + + return new ResponseEntity<>(new UserStats(dossierMemberships.size(), dossierOwnerships.size(), this.fileStatusPersistenceService.getNumberOfAssignedFiles(userId)), + HttpStatus.OK); + } + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java new file mode 100644 index 000000000..86347d586 --- /dev/null +++ b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/UserStatsResource.java @@ -0,0 +1,37 @@ +package com.iqser.red.service.persistence.service.v1.api.external.resource; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +import com.iqser.red.service.persistence.service.v1.api.shared.model.UserStats; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")}) +public interface UserStatsResource { + + String USER_ID_PARAM = "userId"; + + String USER_ID_PATH_VARIABLE = "/{" + USER_ID_PARAM + "}"; + + String STATS_PATH = "/user-stats"; + + String PATH = ExternalApi.BASE_PATH + STATS_PATH; + + + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + @GetMapping(value = PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Gets user stats for user specified by id.", description = "") + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")}) + ResponseEntity getUserStats(@Parameter(name = USER_ID_PARAM, description = "The unique identifier of the user whose statistics we want to retrieve.", required = true) @PathVariable(USER_ID_PARAM) String userId); + +} \ No newline at end of file diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java index 2777ef08e..894179b46 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/acl/custom/dossier/DossierACLService.java @@ -73,6 +73,24 @@ public class DossierACLService extends AbstractACLService { } + public Set getOwners(String dossierId) { + + ObjectIdentityImpl dossierIdentity = new ObjectIdentityImpl(getIdentifier(), dossierId); + var acl = mutableAclService.readAclById(dossierIdentity); + Set members = new HashSet<>(); + acl.getEntries() + .forEach(entry -> { + if (entry.getSid() instanceof PrincipalSid) { + var principal = ((PrincipalSid) entry.getSid()).getPrincipal(); + if (entry.getPermission().getMask() == RedPermission.OWNER.getMask()) { + members.add(principal); + } + } + }); + return members; + } + + @Override public String getIdentifier() { diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java index 6c17e7741..1790b52b0 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ActionRoles.java @@ -168,6 +168,10 @@ public final class ActionRoles { public static final String READ_APP_CONFIG = "red-read-app-configuration"; public static final String WRITE_APP_CONFIG = "red-write-app-configuration"; + // USER STATS + + public static final String READ_USER_STATS = "red-get-user-stats"; + // License Management public static final String UPDATE_LICENSE = "red-update-license"; public static final String READ_LICENSE = "red-read-license"; diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java index aad3cc332..9174cf8e4 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java @@ -10,7 +10,6 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import com.iqser.red.service.persistence.management.v1.processor.entity.projection.DossierStatsFileProjection; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -18,14 +17,15 @@ import com.iqser.red.service.persistence.management.v1.processor.entity.dossier. import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity; import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntityComponentMappingVersionEntity; +import com.iqser.red.service.persistence.management.v1.processor.entity.projection.DossierStatsFileProjection; import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException; import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException; import com.iqser.red.service.persistence.management.v1.processor.model.FileIdentifier; import com.iqser.red.service.persistence.management.v1.processor.model.OCRStatusUpdateResponse; import com.iqser.red.service.persistence.management.v1.processor.model.websocket.AnalyseStatus; -import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileAttributesRepository; import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.FileRepository; +import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo; @@ -415,6 +415,7 @@ public class FileStatusPersistenceService { .toList(); } + public List getActiveFilesForDossierTemplate(String dossierTemplateId) { return dossierService.findAllActiveDossiersForDossierTemplateId(dossierTemplateId) @@ -440,6 +441,7 @@ public class FileStatusPersistenceService { .collect(Collectors.toList()); } + public List getFilesForDossierStats(String dossierId) { return fileRepository.findDossierStatsProjectionFileProjectionByDossierId(dossierId); @@ -500,12 +502,12 @@ public class FileStatusPersistenceService { fileAttributesRepository.deleteByFileIds(fileIds); } + public void hardDeleteFiles(List fileIds, OffsetDateTime hardDeleteTime) { fileRepository.hardDeleteFiles(fileIds, ProcessingStatus.PROCESSED, hardDeleteTime); fileAttributesRepository.deleteByFileIds(fileIds); - } @@ -573,7 +575,8 @@ public class FileStatusPersistenceService { uploader, ProcessingStatus.UNPROCESSED, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), - OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), disableAutomaticAnalysis); + OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), + disableAutomaticAnalysis); } else { fileRepository.findById(fileId) @@ -585,7 +588,8 @@ public class FileStatusPersistenceService { ProcessingStatus.UNPROCESSED, WorkflowStatus.NEW, OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), - OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), disableAutomaticAnalysis); + OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS), + disableAutomaticAnalysis); deleteFileAttributes(fileId); } @@ -595,6 +599,13 @@ public class FileStatusPersistenceService { } + public int getNumberOfAssignedFiles(String userId) { + + List files = fileRepository.findFilesByAssignee(userId); + return files.size(); + } + + @Transactional public void deleteFileAttributes(String fileId) { @@ -647,11 +658,14 @@ public class FileStatusPersistenceService { fileRepository.updateFileModificationDate(fileId, fileManipulationDate); } + @Transactional - public void resetErrorCounter(String dossierTemplateId){ + public void resetErrorCounter(String dossierTemplateId) { + fileRepository.updateErrorCounter(dossierTemplateId, 0); } + @Transactional public void updateHasHighlights(String fileId, boolean hasHighlights) { @@ -726,7 +740,7 @@ public class FileStatusPersistenceService { public List findAllByDossierId(String dossierId, boolean includeDeleted) { - return fileRepository.findAllByDossierId(dossierId, includeDeleted); + return fileRepository.findAllByDossierId(dossierId, includeDeleted); } } diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java index 93cdfde12..6156dd593 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/repository/FileRepository.java @@ -49,6 +49,10 @@ public interface FileRepository extends JpaRepository { @Param("hasUpdates") boolean hasUpdates); + @Query("select f from FileEntity f where f.assignee = :assignee") + List findFilesByAssignee(@Param("assignee") String assignee); + + @Modifying @Query(""" update FileEntity f set f.numberOfPages = :numberOfPages, \ diff --git a/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java new file mode 100644 index 000000000..5ce4f62dc --- /dev/null +++ b/persistence-service-v1/persistence-service-shared-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/model/UserStats.java @@ -0,0 +1,18 @@ +package com.iqser.red.service.persistence.service.v1.api.shared.model; + +import lombok.Data; + +@Data +public class UserStats { + + private int numberOfDossierMemberships; + private int numberOfDossierOwnerships; + private int numberOfAssignedFiles; + public UserStats(int numberOfDossierMemberships, int numberOfDossierOwnerships, int numberOfAssignedFiles) { + + this.numberOfDossierMemberships = numberOfDossierMemberships; + this.numberOfDossierOwnerships = numberOfDossierOwnerships; + this.numberOfAssignedFiles = numberOfAssignedFiles; + } + +} \ No newline at end of file