Merge pull request #187 from Kitzanos/feature/GEPAFINBE-145

GEPAFINBE-145 (Instructor: dashboard stats)
This commit is contained in:
Rinaldo
2025-01-30 10:51:04 +01:00
committed by GitHub
14 changed files with 258 additions and 9 deletions

View File

@@ -2,7 +2,6 @@ package net.gepafin.tendermanagement.dao;
import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.entities.CompanyEntity;
import net.gepafin.tendermanagement.entities.UserActionEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.entities.UserWithCompanyEntity;
import net.gepafin.tendermanagement.entities.*;
@@ -16,12 +15,13 @@ import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -352,8 +352,7 @@ public class DashboardDao {
responseBean.setAverageResponseDays(BigDecimal.ZERO);
}
}
public AssignedApplicationWidgetResponseBean getApplicationDetailsForEvaluation(UserEntity userEntity) {
public AssignedApplicationWidgetResponseBean getApplicationDetailsForEvaluation(UserEntity userEntity) {
Long userId = userEntity.getId();
Long hubId = userEntity.getHub().getId();
@@ -404,6 +403,94 @@ public class DashboardDao {
}
private PreInstructorWidgetResponseBean initializeDashboardPreInstructorResponseBean() {
return PreInstructorWidgetResponseBean.builder()
.assignedApplication(ApplicationToConsider.builder()
.totalAssignedApplication(0L)
.additionalApplicationPercentage(BigDecimal.ZERO)
.build())
.evaluatedApplication(EvaluatedApplication.builder()
.evaluatedApplication(0L)
.dailyAverage(BigDecimal.ZERO)
.build())
.averageEvaluationDays(AverageEvaluationTime.builder()
.averageEvlauationDaysRating(BigDecimal.ZERO)
.timeDifferenceFromAverage(BigDecimal.ZERO)
.build())
.amendmentInProgress(RescueInstructorInProgress.builder()
.totalAmendmentInProgress(0L)
.expiringToday(0L)
.build())
.build();
}
public PreInstructorWidgetResponseBean getDashboardWidgetForPreInstructor(UserEntity userEntity) {
PreInstructorWidgetResponseBean preInstructorWidgetResponseBean = initializeDashboardPreInstructorResponseBean();
Long hubId = userEntity.getHub().getId();
List<Long> applicationIds = getApplicationIdsForUserOrHub(userEntity, hubId);
setPreInstructorWidgets(applicationIds, preInstructorWidgetResponseBean,hubId);
calculateAverageEvaluationTime(applicationIds, preInstructorWidgetResponseBean, hubId);
return preInstructorWidgetResponseBean;
}
private void setPreInstructorWidgets(List<Long> applicationIds, PreInstructorWidgetResponseBean responseBean,Long hubId) {
Long totalAssignedApplications = assignedApplicationsRepository.countAssignedApplicationsByApplicationIds(applicationIds);
if (totalAssignedApplications != null) {
responseBean.getAssignedApplication().setTotalAssignedApplication(totalAssignedApplications);
}
LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
Long newApplicationsAddedYesterday = assignedApplicationsRepository.countApplicationsAddedYesterdayForHub(applicationIds, hubId, yesterday);
if (newApplicationsAddedYesterday != null && totalAssignedApplications != null && totalAssignedApplications > 0) {
BigDecimal percentageAdded = BigDecimal.valueOf(newApplicationsAddedYesterday)
.divide(BigDecimal.valueOf(totalAssignedApplications), 4, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100));
responseBean.getAssignedApplication().setAdditionalApplicationPercentage(percentageAdded.setScale(2, RoundingMode.HALF_UP));
}
List<String> statuses = Arrays.asList(ApplicationStatusTypeEnum.APPROVED.getValue(), ApplicationStatusTypeEnum.REJECTED.getValue());
LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
Long evaluatedApplication = applicationRepository.countEvaluatedApplicationsInLast7Days(applicationIds, statuses, sevenDaysAgo);
if (evaluatedApplication != null) {
responseBean.getEvaluatedApplication().setEvaluatedApplication(evaluatedApplication);
BigDecimal dailyAverage = applicationRepository.countDailyAverageEvaluatedApplicationsInLast7Days(applicationIds, statuses, sevenDaysAgo);
if (dailyAverage != null) {
responseBean.getEvaluatedApplication().setDailyAverage(dailyAverage.setScale(2, RoundingMode.HALF_UP)); // Rounded to 2 decimal places
}
}
Long rescueInstructorsInProgress = assignedApplicationsRepository.countApplicationsByIdsAndStatus(applicationIds, AssignedApplicationEnum.SOCCORSO.getValue());
if (rescueInstructorsInProgress != null) {
responseBean.getAmendmentInProgress().setTotalAmendmentInProgress(rescueInstructorsInProgress);
}
calculateExpiringRescueInstructorsToday(applicationIds,responseBean);
}
private void calculateExpiringRescueInstructorsToday(List<Long> applicationIds, PreInstructorWidgetResponseBean responseBean) {
List<String> statuses =Arrays.asList( ApplicationAmendmentRequestEnum.AWAITING.getValue(), ApplicationAmendmentRequestEnum.RESPONSE_RECEIVED.getValue());
LocalDateTime startOfDay = LocalDateTime.now().toLocalDate().atStartOfDay();
LocalDateTime endOfDay = startOfDay.plusDays(1).minusNanos(1);
Long expiringToday = applicationAmendmentRequestRepository.countAmendmentsExpiringToday(applicationIds, statuses, startOfDay, endOfDay);
if (expiringToday != null) {
responseBean.getAmendmentInProgress().setExpiringToday(expiringToday);
}
}
private void calculateAverageEvaluationTime(List<Long> applicationIds, PreInstructorWidgetResponseBean responseBean, Long hubId) {
if (Boolean.FALSE.equals(applicationIds.isEmpty())) {
BigDecimal averageTime = applicationEvaluationRepository.findAverageEvaluationTimeByApplicationIds(applicationIds);
responseBean.getAverageEvaluationDays().setAverageEvlauationDaysRating(
averageTime != null ? averageTime.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
);
//
// BigDecimal timeDifference = applicationEvaluationRepository.findTimeDifferenceFromAverage(applicationIds);
// responseBean.getAverageEvaluationTime().setTimeDifferenceFromAverage(timeDifference != null ? timeDifference : BigDecimal.ZERO);
}
}
}

View File

@@ -146,6 +146,7 @@ public enum UserActionContextEnum {
GET_DASHBOARD_WIDGET_FOR_BENEFICIARY("GET_DASHBOARD_WIDGET_FOR_BENEFICIARY"),
GET_APPLICATION_DETAILS("GET_APPLICATION_DETAILS"),
GET_AMENDMENT_DETAILS("GET_AMENDMENT_DETAILS"),
GET_DASHBOARD_WIDGET_FOR_PRE_INSTRUCTOR("GET_DASHBOARD_WIDGET_FOR_PRE_INSTRUCTOR"),
GET_APPLICATION_DETAILS_FOR_EVALUATION("GET_APPLICATION_DETAILS_FOR_EVALUATION"),
/** Evaluation criteria action context **/

View File

@@ -0,0 +1,15 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
@Builder
@Data
public class ApplicationToConsider {
public Long totalAssignedApplication;
private BigDecimal additionalApplicationPercentage;
}

View File

@@ -0,0 +1,15 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
@Builder
@Data
public class AverageEvaluationTime {
private BigDecimal averageEvlauationDaysRating;
private BigDecimal timeDifferenceFromAverage;
}

View File

@@ -0,0 +1,15 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
@Builder
@Data
public class EvaluatedApplication {
public Long evaluatedApplication;
private BigDecimal dailyAverage;
}

View File

@@ -0,0 +1,20 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
@Builder
@Data
public class PreInstructorWidgetResponseBean {
private ApplicationToConsider assignedApplication;
private EvaluatedApplication evaluatedApplication;
private AverageEvaluationTime averageEvaluationDays;
private RescueInstructorInProgress amendmentInProgress;
}

View File

@@ -0,0 +1,13 @@
package net.gepafin.tendermanagement.model.response;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
public class RescueInstructorInProgress {
private Long totalAmendmentInProgress;
private Long expiringToday;
}

View File

@@ -132,4 +132,20 @@ public interface ApplicationAmendmentRequestRepository extends JpaRepository<App
@Param("startDate") LocalDateTime startDate,
@Param("endDate") LocalDateTime endDate
);
@Query(value = """
SELECT COUNT(*)
FROM {h-schema}application_amendment_request e
WHERE e.application_id IN :applicationIds
AND e.status IN :statuses
AND e.start_date + INTERVAL '1 DAY' * e.response_days >= :startOfDay
AND e.start_date + INTERVAL '1 DAY' * e.response_days < :endOfDay
AND e.is_deleted = false
""", nativeQuery = true)
Long countAmendmentsExpiringToday(
@Param("applicationIds") List<Long> applicationIds,
@Param("statuses") List<String> statuses,
@Param("startOfDay") LocalDateTime startOfDay,
@Param("endOfDay") LocalDateTime endOfDay
);
}

View File

@@ -8,6 +8,8 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@@ -92,5 +94,16 @@ public interface ApplicationRepository extends JpaRepository<ApplicationEntity,
@Query("SELECT a.id FROM ApplicationEntity a WHERE a.hubId = :hubId AND a.isDeleted = false")
List<Long> findApplicationIdsByHubId(@Param("hubId") Long hubId);
@Query("SELECT COUNT(a) FROM ApplicationEntity a WHERE a.id IN :applicationIds AND a.status IN :statuses AND a.isDeleted = false AND a.updatedDate >= :sevenDaysAgo")
Long countEvaluatedApplicationsInLast7Days(@Param("applicationIds") List<Long> applicationIds,
@Param("statuses") List<String> statuses,
@Param("sevenDaysAgo") LocalDateTime sevenDaysAgo);
@Query("SELECT (COUNT(a) / 7.0) FROM ApplicationEntity a WHERE a.id IN :applicationIds AND a.status IN :statuses AND a.isDeleted = false AND a.updatedDate >= :sevenDaysAgo")
BigDecimal countDailyAverageEvaluatedApplicationsInLast7Days(@Param("applicationIds") List<Long> applicationIds,
@Param("statuses") List<String> statuses,
@Param("sevenDaysAgo") LocalDateTime sevenDaysAgo);
}

View File

@@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@@ -26,6 +27,23 @@ public interface AssignedApplicationsRepository extends JpaRepository<AssignedAp
@Query("SELECT aa.application.id FROM AssignedApplicationsEntity aa WHERE aa.isDeleted = false AND aa.userId = :userId")
List<Long> findApplicationIdsByUserIdAndIsDeletedFalse(@Param("userId") Long userId);
@Query("SELECT COUNT(a) FROM AssignedApplicationsEntity a WHERE a.application.id IN :applicationIds AND a.isDeleted = false")
Long countAssignedApplicationsByApplicationIds(@Param("applicationIds") List<Long> applicationIds);
@Query("""
SELECT COUNT(aa)
FROM AssignedApplicationsEntity aa
JOIN aa.application a
WHERE a.hubId = :hubId
AND a.isDeleted = false
AND aa.createdDate >= :yesterday
AND (:applicationIds IS NULL OR aa.application.id IN :applicationIds)
""")
Long countApplicationsAddedYesterdayForHub(@Param("applicationIds") List<Long> applicationIds, @Param("hubId") Long hubId, @Param("yesterday") LocalDateTime yesterday);
@Query("SELECT COUNT(a) FROM AssignedApplicationsEntity a WHERE a.application.id IN :applicationIds AND a.status = :status AND a.isDeleted = false")
Long countApplicationsByIdsAndStatus(@Param("applicationIds") List<Long> applicationIds, @Param("status") String status);
@Query("""
SELECT
COALESCE(COUNT(a.id), 0) AS totalAssigned,

View File

@@ -1,6 +1,7 @@
package net.gepafin.tendermanagement.service;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.model.response.*;
import net.gepafin.tendermanagement.model.response.AmendmentWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.AssignedApplicationWidgetResponseBean;
@@ -14,6 +15,7 @@ public interface DashboardService {
public BeneficiaryWidgetResponseBean getDashboardWidgetForBeneficiary(HttpServletRequest request, Long companyId);
public ApplicationWidgetResponseBean getApplicationDetails(HttpServletRequest request);
public AmendmentWidgetResponseBean getAmendmentDetails(HttpServletRequest request);
public PreInstructorWidgetResponseBean getDashboardWidgetForPreInstructor(HttpServletRequest request);
public AssignedApplicationWidgetResponseBean getApplicationDetailsForEvaluation(HttpServletRequest request);
}

View File

@@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.dao.DashboardDao;
import net.gepafin.tendermanagement.entities.CompanyEntity;
import net.gepafin.tendermanagement.entities.UserEntity;
import net.gepafin.tendermanagement.model.response.*;
import net.gepafin.tendermanagement.model.response.AmendmentWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.AssignedApplicationWidgetResponseBean;
@@ -46,7 +47,11 @@ public class DashboardServiceImpl implements DashboardService {
UserEntity userEntity=validator.validateUser(request);
return dashboardDao.getAmendmentDetails(userEntity);
}
@Override
public PreInstructorWidgetResponseBean getDashboardWidgetForPreInstructor(HttpServletRequest request) {
UserEntity userEntity = validator.validateUser(request);
return dashboardDao.getDashboardWidgetForPreInstructor(userEntity);
}
@Override
public AssignedApplicationWidgetResponseBean getApplicationDetailsForEvaluation(HttpServletRequest request) {
UserEntity userEntity = validator.validateUser(request);

View File

@@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import net.gepafin.tendermanagement.model.response.*;
import net.gepafin.tendermanagement.model.response.AmendmentWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.AssignedApplicationWidgetResponseBean;
@@ -77,18 +78,36 @@ public interface DashboardApi {
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')|| hasRole('ROLE_PRE_INSTRUCTOR')")
ResponseEntity<Response<AmendmentWidgetResponseBean>> getAmendmentDetails(HttpServletRequest request);
@Operation(summary = "Api to get Application details for Evaluation",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })),
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) })
@GetMapping(value = "/evaluation",
@GetMapping(value = "/evaluation",
produces = { "application/json" })
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')|| hasRole('ROLE_PRE_INSTRUCTOR')")
ResponseEntity<Response<AssignedApplicationWidgetResponseBean>> getApplicationDetailsForEvaluation(HttpServletRequest request);
@Operation(summary = "Api to get dashboard widget for pre instructor",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE) })),
@ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE) })),
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = {
@ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE) })) })
@GetMapping(value = "/instructor/amendment",
produces = { "application/json" })
@PreAuthorize("hasRole('ROLE_SUPER_ADMIN') || hasRole('ROLE_INSTRUCTOR_MANAGER')|| hasRole('ROLE_PRE_INSTRUCTOR')")
ResponseEntity<Response<PreInstructorWidgetResponseBean>> getDashboardWidgetForPreInstructor(HttpServletRequest request);
}

View File

@@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.constants.GepafinConstant;
import net.gepafin.tendermanagement.enums.UserActionContextEnum;
import net.gepafin.tendermanagement.enums.UserActionLogsEnum;
import net.gepafin.tendermanagement.model.request.UserActionRequest;
import net.gepafin.tendermanagement.model.response.*;
import net.gepafin.tendermanagement.model.response.AmendmentWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.ApplicationWidgetResponseBean;
import net.gepafin.tendermanagement.model.response.AssignedApplicationWidgetResponseBean;
@@ -74,6 +75,15 @@ public class DashboardApiController implements DashboardApi {
}
@Override
public ResponseEntity<Response<PreInstructorWidgetResponseBean>> getDashboardWidgetForPreInstructor(HttpServletRequest request) {
/** This code is responsible for creating user action logs for the "Get dashboard stats widget for amendment page" operation. **/
loggingUtil.logUserAction(UserActionRequest.builder().request(request).actionType(UserActionLogsEnum.VIEW).actionContext(UserActionContextEnum.GET_DASHBOARD_WIDGET_FOR_PRE_INSTRUCTOR).build());
PreInstructorWidgetResponseBean widgetResponseBean= dashboardService.getDashboardWidgetForPreInstructor(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(new Response<>(widgetResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.DASHBOARD_WIDGET_FETCHED_SUCCESSFULLY)));
}
public ResponseEntity<Response<AssignedApplicationWidgetResponseBean>> getApplicationDetailsForEvaluation(HttpServletRequest request) {
/** This code is responsible for creating user action logs for the "Get Application Details for Evaluation" operation. **/