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

fix(SILVA-515): updating submission trends #525

Merged
merged 37 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a69e5d5
fix(SILVA-515): updated submission trends backend
paulushcgcj Dec 5, 2024
fab1e69
fix(SILVA-515): updated submission trends endpoint
paulushcgcj Dec 5, 2024
41bedd5
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 5, 2024
1f50d06
fix(SILVA-515): fixed timeframe on default values
paulushcgcj Dec 5, 2024
0657431
fix(SILVA-515): fixed timeframe on default values
paulushcgcj Dec 5, 2024
98af226
fix(SILVA-515): fixing frontend dashboard
paulushcgcj Dec 5, 2024
7da3d28
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 6, 2024
993007e
fix(SILVA-584): fixing query count
paulushcgcj Dec 6, 2024
b0605fd
fix(SILVA-515): fixing layout
paulushcgcj Dec 6, 2024
668ad12
chore: fixed multi select text
paulushcgcj Dec 6, 2024
32269ad
fix(SILVA-515): adding count of status
paulushcgcj Dec 6, 2024
bccae3b
fix(SILVA-515): added new fields and test update
paulushcgcj Dec 6, 2024
4e6d7bd
chore: updating tooltip
paulushcgcj Dec 6, 2024
d3aaef3
chore added empty section
paulushcgcj Dec 6, 2024
5e7cac1
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 6, 2024
b02537e
chore: added initial empty state
paulushcgcj Dec 6, 2024
6d279fc
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 6, 2024
3889e6c
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 6, 2024
b3fbba2
chore: updating tooltip
paulushcgcj Dec 6, 2024
de99a19
chore: fixing sonar issues
paulushcgcj Dec 6, 2024
8d4d513
chore: fixing sonar issues
paulushcgcj Dec 6, 2024
9a612b1
test: improving tests
paulushcgcj Dec 6, 2024
42980c6
chore: fixing sonar issues
paulushcgcj Dec 6, 2024
7e8ef00
test: improving tests
paulushcgcj Dec 6, 2024
ea343d6
chore: fixing build
paulushcgcj Dec 9, 2024
dd38d7d
test: updating test configuration
paulushcgcj Dec 9, 2024
41cef7d
test: adding tests
paulushcgcj Dec 9, 2024
471ae48
Merge branch 'main' into fix/SILVA-515
paulushcgcj Dec 9, 2024
e79b200
fix(SILVA-515): adding more data to opening trends
paulushcgcj Dec 10, 2024
0bfc3dc
fix(SILVA-515): added missing carbon charts dependency
paulushcgcj Dec 10, 2024
6a43e71
fix(SILVA-515): fixing tooltip to match expectation
paulushcgcj Dec 10, 2024
1d898df
test: fixing tests
paulushcgcj Dec 10, 2024
7a94320
feat(SILVA-515): adding silviculture search through navigation
paulushcgcj Dec 10, 2024
56f32a7
chore: spacing tooltip
paulushcgcj Dec 10, 2024
a1990bd
chore: fixing test issue
paulushcgcj Dec 10, 2024
b9b7dde
chore: adding tests
paulushcgcj Dec 10, 2024
308ff76
chore: added auto search for query strings
paulushcgcj Dec 11, 2024
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,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
Loading