Skip to content

Commit

Permalink
fix(SILVA-515): updated submission trends backend
Browse files Browse the repository at this point in the history
  • Loading branch information
paulushcgcj committed Dec 5, 2024
1 parent 0978fce commit a69e5d5
Show file tree
Hide file tree
Showing 10 changed files with 397 additions and 237 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()),
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()).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 @@ -351,4 +352,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,81 @@
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.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),
orgUnits == null ? List.of("NOVALUE") : orgUnits,
statusCodes == null ? List.of("NOVALUE") : statusCodes
);

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

// Map to count entries grouped by month
Map<Integer, Long> monthToCountMap = entities.stream()
.filter(entity -> entity.getEntryTimestamp() != null) // Ensure timestamp is not null
.collect(Collectors.groupingBy(
entity -> entity.getEntryTimestamp().getMonthValue(), // Extract month value
Collectors.counting() // Count occurrences
));

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

// Generate the DTOs in the custom order
return monthSequence.stream()
.map(month -> new OpeningsPerYearDto(
month,
getMonthName(month),
monthToCountMap.getOrDefault(month, 0L) // Use 0 if no entries for this month
))
.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
Expand Up @@ -11,7 +11,7 @@
public record OpeningsPerYearDto(
Integer month,
String monthName,
Integer amount
Long amount
) {

}
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@
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.MyRecentActionsRequestsDto;
import ca.bc.gov.restapi.results.postgres.dto.OpeningsPerYearDto;
import ca.bc.gov.restapi.results.postgres.entity.OpeningsActivityEntity;
import ca.bc.gov.restapi.results.postgres.entity.OpeningsLastYearEntity;
import ca.bc.gov.restapi.results.postgres.repository.OpeningsActivityRepository;
import ca.bc.gov.restapi.results.postgres.repository.OpeningsLastYearRepository;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -28,7 +25,9 @@
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

/** This class contains methods for gathering and grouping data for the dashboard metrics screen. */
/**
* This class contains methods for gathering and grouping data for the dashboard metrics screen.
*/
@Slf4j
@Service
@RequiredArgsConstructor
Expand All @@ -42,41 +41,6 @@ public class DashboardMetricsService {

private final PrettyTime prettyTime = new PrettyTime();

/**
* Get openings submission trends data for the opening per year chart.
*
* @param filters Possible filter, see {@link DashboardFiltersDto} for more.
* @return A list of {@link OpeningsPerYearDto} for the opening chart.
*/
public List<OpeningsPerYearDto> getOpeningsSubmissionTrends(DashboardFiltersDto filters) {
log.info("Getting Opening Submission Trends with filters {}", filters.toString());

LocalDateTime baseDateTime = LocalDateTime.now().minusMonths(12);
List<OpeningsLastYearEntity> entities =
openingsLastYearRepository.findAllFromLastYear(
baseDateTime, Sort.by("entryTimestamp").ascending());

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

Map<Integer, String> monthNamesMap = new HashMap<>();
Map<Integer, List<OpeningsLastYearEntity>> resultMap =
createBaseMonthsMap(monthNamesMap, entities.get(0).getEntryTimestamp().getMonthValue());

filterOpeningSubmissions(resultMap, entities, filters);

List<OpeningsPerYearDto> chartData = new ArrayList<>();
for (Integer monthKey : resultMap.keySet()) {
List<OpeningsLastYearEntity> monthDataList = resultMap.get(monthKey);
String monthName = monthNamesMap.get(monthKey);
log.info("Value {} for the month: {}", monthDataList.size(), monthName);
chartData.add(new OpeningsPerYearDto(monthKey, monthName, monthDataList.size()));
}

return chartData;
}

/**
* Get free growing milestone declarations data for the chart.
Expand Down
Loading

0 comments on commit a69e5d5

Please sign in to comment.