Skip to content

Commit

Permalink
fix(SILVA-515): updating submission trends (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulushcgcj authored Dec 11, 2024
1 parent d24b4f2 commit 7d27787
Show file tree
Hide file tree
Showing 35 changed files with 1,809 additions and 1,019 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package ca.bc.gov.restapi.results.oracle.endpoint;

import ca.bc.gov.restapi.results.oracle.service.OpeningTrendsService;
import ca.bc.gov.restapi.results.oracle.service.UserActionsService;
import ca.bc.gov.restapi.results.postgres.dto.MyRecentActionsRequestsDto;
import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand All @@ -17,6 +21,7 @@
public class UserActionsEndpoint {

private final UserActionsService userActionsService;
private final OpeningTrendsService openingTrendsService;

@GetMapping("/recent-actions")
public ResponseEntity<List<MyRecentActionsRequestsDto>> getUserRecentOpeningsActions() {
Expand All @@ -32,4 +37,49 @@ public ResponseEntity<List<MyRecentActionsRequestsDto>> getUserRecentOpeningsAct
return ResponseEntity.ok(actionsDto);
}

/**
* Gets data for the Opening submission trends Chart (Openings per year) on the Dashboard SILVA
* page.
*
* @param orgUnits Optional district code filter.
* @param statusCodes Optional opening status code filter.
* @param entryDateStart Optional opening entry timestamp start date filter.
* @param entryDateEnd Optional opening entry timestamp end date filter.
* @return A list of values to populate the chart or 204 no content if no data.
*/
@GetMapping("/submission-trends")
public ResponseEntity<List<OpeningsPerYearDto>> getOpeningsSubmissionTrends(
@RequestParam(value = "orgUnitCode", required = false)
List<String> orgUnits,
@RequestParam(value = "statusCode", required = false)
List<String> statusCodes,
@RequestParam(value = "entryDateStart", required = false)
LocalDate entryDateStart,
@RequestParam(value = "entryDateEnd", required = false)
LocalDate entryDateEnd
) {

List<OpeningsPerYearDto> resultList =
openingTrendsService.getOpeningSubmissionTrends(
getDateOrDefault(entryDateStart,LocalDate.now().minusYears(1)),
getDateOrDefault(entryDateEnd,
//If we have an end date, we get it, otherwise we use the current date,
// and no matter if we have the start date or not, we add a year to the end date
getDateOrDefault(entryDateStart,LocalDate.now().minusYears(1)).plusYears(1)
),
orgUnits,
statusCodes
);

if (resultList.isEmpty()) {
return ResponseEntity.noContent().build();
}

return ResponseEntity.ok(resultList);
}

private LocalDate getDateOrDefault(LocalDate date, LocalDate defaultDate) {
return date != null ? date : defaultDate;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ca.bc.gov.restapi.results.oracle.entity;

import java.time.LocalDateTime;

public interface OpeningTrendsProjection {
Long getOpeningId();
String getUserId();
LocalDateTime getEntryTimestamp();
LocalDateTime getUpdateTimestamp();
String getStatus();
String getOrgUnitCode();
String getOrgUnitName();
String getClientNumber();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import ca.bc.gov.restapi.results.oracle.dto.DashboardStockingEventDto;
import ca.bc.gov.restapi.results.oracle.dto.OpeningSearchFiltersDto;
import ca.bc.gov.restapi.results.oracle.entity.OpeningEntity;
import ca.bc.gov.restapi.results.oracle.entity.OpeningTrendsProjection;
import ca.bc.gov.restapi.results.oracle.entity.SilvicultureSearchProjection;
import java.util.List;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -404,4 +405,34 @@ Page<SilvicultureSearchProjection> searchByOpeningIds(
List<Long> openingIds,
Pageable pageable
);

@Query(
nativeQuery = true,
value = """
SELECT
o.OPENING_ID,
o.ENTRY_USERID as user_id,
o.ENTRY_TIMESTAMP,
o.UPDATE_TIMESTAMP,
o.OPENING_STATUS_CODE as status,
ou.ORG_UNIT_CODE AS org_unit_code,
ou.ORG_UNIT_NAME AS org_unit_name,
res.CLIENT_NUMBER AS client_number
FROM THE.OPENING o
LEFT JOIN THE.ORG_UNIT ou ON (ou.ORG_UNIT_NO = o.ADMIN_DISTRICT_NO)
LEFT JOIN THE.RESULTS_ELECTRONIC_SUBMISSION res ON (res.RESULTS_SUBMISSION_ID = o.RESULTS_SUBMISSION_ID)
WHERE
(
o.ENTRY_TIMESTAMP BETWEEN TO_TIMESTAMP(:startDate, 'YYYY-MM-DD') AND TO_TIMESTAMP(:endDate, 'YYYY-MM-DD')
OR o.UPDATE_TIMESTAMP BETWEEN TO_TIMESTAMP(:startDate, 'YYYY-MM-DD') AND TO_TIMESTAMP(:endDate, 'YYYY-MM-DD')
)
AND ( 'NOVALUE' in (:statusList) OR o.OPENING_STATUS_CODE IN (:statusList) )
AND ( 'NOVALUE' in (:orgUnitList) OR ou.ORG_UNIT_CODE IN (:orgUnitList) )
ORDER BY o.ENTRY_TIMESTAMP""")
List<OpeningTrendsProjection> getOpeningTrends(
String startDate,
String endDate,
List<String> statusList,
List<String> orgUnitList
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package ca.bc.gov.restapi.results.oracle.service;

import ca.bc.gov.restapi.results.oracle.entity.OpeningTrendsProjection;
import ca.bc.gov.restapi.results.oracle.repository.OpeningRepository;
import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto;
import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class OpeningTrendsService {

private final OpeningRepository openingRepository;

public List<OpeningsPerYearDto> getOpeningSubmissionTrends(
LocalDate startDate,
LocalDate endDate,
List<String> orgUnits,
List<String> statusCodes
) {

// if the difference between the start date and the end date is bigger than 12, thrown an exception
if (ChronoUnit.MONTHS.between(startDate, endDate) > 12) {
throw new IllegalArgumentException("The date range must be within 12 months");
}

List<OpeningTrendsProjection> entities =
openingRepository.getOpeningTrends(
startDate.format(DateTimeFormatter.ISO_DATE),
endDate.format(DateTimeFormatter.ISO_DATE),
statusCodes == null ? List.of("NOVALUE") : statusCodes,
orgUnits == null ? List.of("NOVALUE") : orgUnits
);

if (entities.isEmpty()) {
log.info("No Opening Submission Trends data found!");
return List.of();
}

// Group by month and status
Map<Integer, Map<String, Long>> monthToStatusCountMap = entities.stream()
.filter(entity -> entity.getEntryTimestamp() != null) // Ensure timestamp is not null
.collect(Collectors.groupingBy(
entity -> entity.getEntryTimestamp().getMonthValue(), // Extract month value
Collectors.groupingBy(
OpeningTrendsProjection::getStatus, // Group by status
Collectors.counting() // Count occurrences
)
));

// Map to count total entries grouped by month
Map<Integer, Long> monthToCountMap = monthToStatusCountMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey, // Month
entry -> entry.getValue().values().stream().mapToLong(Long::longValue).sum() // Sum counts per status
));

// Generate a 12-month sequence starting from the start date
List<YearMonth> yearMonths = IntStream.range(0, 12) // Always 12 months
.mapToObj(offset -> YearMonth.from(startDate).plusMonths(offset))
.toList();

// Generate the DTOs in the custom order
return yearMonths.stream()
.map(yearMonth -> new OpeningsPerYearDto(
yearMonth.getMonthValue(),
yearMonth.getYear(),
getMonthName(yearMonth.getMonthValue()),
monthToCountMap.getOrDefault(yearMonth.getMonthValue(), 0L), // Total count for the month
monthToStatusCountMap.getOrDefault(yearMonth.getMonthValue(), Collections.emptyMap()) // Status counts map
))
.toList();
}


private String getMonthName(int month) {
return Month.of(month).getDisplayName(TextStyle.SHORT, Locale.CANADA);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ca.bc.gov.restapi.results.postgres.dto;

import java.util.Map;
import lombok.Builder;
import lombok.With;

Expand All @@ -10,8 +11,10 @@
@With
public record OpeningsPerYearDto(
Integer month,
Integer year,
String monthName,
Integer amount
Long amount,
Map<String,Long> statusCounts
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import ca.bc.gov.restapi.results.common.util.TimestampUtil;
import ca.bc.gov.restapi.results.postgres.dto.DashboardFiltersDto;
import ca.bc.gov.restapi.results.postgres.dto.FreeGrowingMilestonesDto;
import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto;
import ca.bc.gov.restapi.results.postgres.service.DashboardMetricsService;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -23,43 +22,6 @@ public class DashboardMetricsEndpoint {

private final DashboardMetricsService dashboardMetricsService;

/**
* Gets data for the Opening submission trends Chart (Openings per year) on the Dashboard SILVA
* page.
*
* @param orgUnitCode Optional district code filter.
* @param statusCode Optional opening status code filter.
* @param entryDateStart Optional opening entry timestamp start date filter.
* @param entryDateEnd Optional opening entry timestamp end date filter.
* @return A list of values to populate the chart or 204 no content if no data.
*/
@GetMapping("/submission-trends")
public ResponseEntity<List<OpeningsPerYearDto>> getOpeningsSubmissionTrends(
@RequestParam(value = "orgUnitCode", required = false)
String orgUnitCode,
@RequestParam(value = "statusCode", required = false)
String statusCode,
@RequestParam(value = "entryDateStart", required = false)
String entryDateStart,
@RequestParam(value = "entryDateEnd", required = false)
String entryDateEnd) {
DashboardFiltersDto filtersDto =
new DashboardFiltersDto(
orgUnitCode,
statusCode,
TimestampUtil.parseDateString(entryDateStart),
TimestampUtil.parseDateString(entryDateEnd),
null);

List<OpeningsPerYearDto> resultList =
dashboardMetricsService.getOpeningsSubmissionTrends(filtersDto);

if (resultList.isEmpty()) {
return ResponseEntity.noContent().build();
}

return ResponseEntity.ok(resultList);
}

/**
* Gets data for the Free growing Chart on the Dashboard SILVA page.
Expand Down
Loading

0 comments on commit 7d27787

Please sign in to comment.