Skip to content

Commit

Permalink
Merge pull request #108 from Happy-HOBAK/feat/#106
Browse files Browse the repository at this point in the history
[#106] ์ „์ฒด, ์—ฐ๊ฐ„, ์›”๊ฐ„ ๋ชจ๋“  ์‹œ๊ฐ„๋Œ€ ๋ฐ์ดํ„ฐ ๋žญํ‚น ์กฐํšŒ API ๊ตฌ์ถ•
  • Loading branch information
KkomSang authored May 29, 2024
2 parents b129f44 + 11229af commit 803b799
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
package com.hobak.happinessql.domain.report.api;


import com.hobak.happinessql.domain.report.application.ReportActivityRankingService;
import com.hobak.happinessql.domain.report.application.ReportGraphService;
import com.hobak.happinessql.domain.report.application.ReportLocationRankingService;
import com.hobak.happinessql.domain.report.application.ReportSummaryService;
import com.hobak.happinessql.domain.report.dto.ActivityHappinessDto;
import com.hobak.happinessql.domain.report.dto.LocationHappinessDto;
import com.hobak.happinessql.domain.report.dto.ReportGraphResponseDto;
import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto;
import com.hobak.happinessql.domain.report.application.*;
import com.hobak.happinessql.domain.report.dto.*;
import com.hobak.happinessql.domain.user.application.UserFindService;
import com.hobak.happinessql.domain.user.domain.User;
import com.hobak.happinessql.global.response.DataResponseDto;
Expand All @@ -33,6 +27,7 @@ public class ReportController {
private final ReportSummaryService reportSummaryService;
private final ReportActivityRankingService reportActivityRankingService;
private final ReportLocationRankingService reportLocationRankingService;
private final ReportTimeOfPeriodRankingService reportTimeOfPeriodRankingService;
private final ReportGraphService reportGraphService;
@Operation(summary = "[์ „์ฒด] ํ–‰๋ณต ์ข…ํ•ฉ ๋ฆฌํฌํŠธ", description = "์ „์ฒด ๊ธฐ๊ฐ„์—์„œ ์–ธ์ œ, ์–ด๋””์—์„œ, ๋ฌด์—‡์„ ํ•  ๋•Œ ํ–‰๋ณตํ–ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ๋ฆฌํฌํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping("/all/summary")
Expand Down Expand Up @@ -167,5 +162,29 @@ public DataResponseDto<List<LocationHappinessDto>> getMonthlyLocationRankings(@A
List<LocationHappinessDto> responseDto = reportLocationRankingService.getMonthlyLocationRankings(user);
return DataResponseDto.of(responseDto, "์›”๊ฐ„ ์œ„์น˜ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์กฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค.");
}

@Operation(summary = "[์ „์ฒด] ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„", description = "์ „์ฒด ๊ธฐ๋ก์—์„œ ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping("/all/ranking/time")
public DataResponseDto<List<TimeOfDayHappinessDto>> getAllTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) {
User user = userFindService.findByUserDetails(userDetails);
List<TimeOfDayHappinessDto> responseDto = reportTimeOfPeriodRankingService.getAllTimeOfDayRankings(user);
return DataResponseDto.of(responseDto, "์ „์ฒด ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์กฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค.");
}

@Operation(summary = "[์—ฐ๊ฐ„] ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„", description = "์ด๋ฒˆ ํ•ด ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping("/year/ranking/time")
public DataResponseDto<List<TimeOfDayHappinessDto>> getYearlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) {
User user = userFindService.findByUserDetails(userDetails);
List<TimeOfDayHappinessDto> responseDto = reportTimeOfPeriodRankingService.getYearlyTimeOfDayRankings(user);
return DataResponseDto.of(responseDto, "์—ฐ๊ฐ„ ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์กฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค.");
}

@Operation(summary = "[์›”๊ฐ„] ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„", description = "์ด๋ฒˆ ๋‹ฌ ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping("/month/ranking/time")
public DataResponseDto<List<TimeOfDayHappinessDto>> getMonthlyTimeOfDayRankings(@AuthenticationPrincipal UserDetails userDetails) {
User user = userFindService.findByUserDetails(userDetails);
List<TimeOfDayHappinessDto> responseDto = reportTimeOfPeriodRankingService.getMonthlyTimeOfDayRankings(user);
return DataResponseDto.of(responseDto, "์›”๊ฐ„ ์‹œ๊ฐ„๋Œ€ ํ–‰๋ณต๋„ ์ˆœ์œ„๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ์กฐํšŒํ–ˆ์Šต๋‹ˆ๋‹ค.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.hobak.happinessql.domain.record.domain.Record;
import com.hobak.happinessql.domain.record.repository.RecordRepository;
import com.hobak.happinessql.domain.report.converter.ReportConverter;
import com.hobak.happinessql.domain.report.domain.TimePeriod;
import com.hobak.happinessql.domain.report.domain.TimeOfDay;
import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto;
import com.hobak.happinessql.domain.user.domain.User;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -45,9 +45,9 @@ public ReportSummaryResponseDto getMonthlySummary(User user) {

private ReportSummaryResponseDto generateReportSummary(List<Record> records) {
String location = LocationHappinessAnalyzer.getHappiestLocation(records);
TimePeriod timePeriod = TimePeriodHappinessAnalyzer.getHappiestTimePeriod(records);
TimeOfDay timeOfDay = TimeOfDayHappinessAnalyzer.getHappiestTimeOfDay(records);
String activity = ActivityHappinessAnalyzer.getHappiestActivity(records);

return ReportConverter.toReportSummaryResponseDto(timePeriod, location, activity);
return ReportConverter.toReportSummaryResponseDto(timeOfDay, location, activity);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.hobak.happinessql.domain.report.application;

import com.hobak.happinessql.domain.record.domain.Record;
import com.hobak.happinessql.domain.record.repository.RecordRepository;
import com.hobak.happinessql.domain.report.dto.TimeOfDayHappinessDto;
import com.hobak.happinessql.domain.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ReportTimeOfPeriodRankingService {

private final RecordRepository recordRepository;

public List<TimeOfDayHappinessDto> getAllTimeOfDayRankings(User user) {
List<Record> records = recordRepository.findAllByUser(user);
return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records);
}

public List<TimeOfDayHappinessDto> getYearlyTimeOfDayRankings(User user) {
int currentYear = LocalDate.now().getYear();
LocalDateTime startOfYear = LocalDateTime.of(currentYear, 1, 1, 0, 0);
LocalDateTime endOfYear = LocalDateTime.of(currentYear, 12, 31, 23, 59, 59);
List<Record> records = recordRepository.findAllByCreatedAtBetweenAndUser(startOfYear, endOfYear, user);
return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records);
}

public List<TimeOfDayHappinessDto> getMonthlyTimeOfDayRankings(User user) {
LocalDate today = LocalDate.now();
LocalDate startOfMonth = today.withDayOfMonth(1);
LocalDate endOfMonth = today.withDayOfMonth(today.lengthOfMonth());
LocalDateTime startOfMonthDateTime = startOfMonth.atStartOfDay();
LocalDateTime endOfMonthDateTime = endOfMonth.atTime(23, 59, 59);
List<Record> records = recordRepository.findAllByCreatedAtBetweenAndUser(startOfMonthDateTime, endOfMonthDateTime, user);
return TimeOfDayHappinessAnalyzer.getTimeOfDayRankings(records);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.hobak.happinessql.domain.report.application;

import com.hobak.happinessql.domain.record.domain.Record;
import com.hobak.happinessql.domain.report.converter.ReportConverter;
import com.hobak.happinessql.domain.report.domain.TimeOfDay;
import com.hobak.happinessql.domain.report.dto.TimeOfDayHappinessDto;

import java.util.*;
import java.util.stream.Collectors;

public class TimeOfDayHappinessAnalyzer {

public static TimeOfDay getHappiestTimeOfDay(List<Record> records) {
if (records == null || records.isEmpty()) {
return null;
}

// ์‹œ๊ฐ„๋Œ€๋ฅผ ๊ธฐ์ค€์œผ๋กœ Record ๊ทธ๋ฃนํ™”
Map<TimeOfDay, List<Record>> timeOfDayRecordsMap = groupRecordsByTimeOfDay(records);

// ์‹œ๊ฐ„๋Œ€๋ณ„ ํ‰๊ท  ํ–‰๋ณต๋„์™€ ๋นˆ๋„ ๊ณ„์‚ฐ
Map<TimeOfDay, Double> timeOfDayAverageHappiness = calculateTimeOfDayAverageHappiness(timeOfDayRecordsMap);
Map<TimeOfDay, Integer> timeOfDayFrequency = calculateTimeOfDayFrequency(timeOfDayRecordsMap);

// ํ‰๊ท  ํ–‰๋ณต๋„๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ์‹œ๊ฐ„๋Œ€ ์ฐพ๊ธฐ
double maxHappiness = Collections.max(timeOfDayAverageHappiness.values());

// ํ‰๊ท  ํ–‰๋ณต๋„๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ์‹œ๊ฐ„๋Œ€๋“ค ์ค‘ ๋นˆ๋„๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ์‹œ๊ฐ„๋Œ€ ์ฐพ๊ธฐ
Optional<TimeOfDay> happiestTimeOfDay = findHappiestTimeOfDay(timeOfDayAverageHappiness, timeOfDayFrequency, maxHappiness);

// ํ‰๊ท  ํ–‰๋ณต๋„์™€ ๋นˆ๋„๊ฐ€ ๊ฐ™์€ ์‹œ๊ฐ„๋Œ€๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๊ฒฝ์šฐ, ๋žœ๋ค์œผ๋กœ ์„ ํƒ
List<TimeOfDay> happiestTimesOfDay = findTimesOfDayWithMaxHappiness(timeOfDayAverageHappiness, maxHappiness);

if (happiestTimesOfDay.size() > 1) {
Collections.shuffle(happiestTimesOfDay);
return happiestTimesOfDay.get(0);
} else {
return happiestTimeOfDay.orElse(null);
}
}

public static List<TimeOfDayHappinessDto> getTimeOfDayRankings(List<Record> records) {
List<TimeOfDayHappinessDto> timeOfDayRankings = new ArrayList<>();

// ์‹œ๊ฐ„๋Œ€๋ณ„ ํ‰๊ท  ํ–‰๋ณต๋„์™€ ๋นˆ๋„ ๊ณ„์‚ฐ
Map<TimeOfDay, List<Record>> timeOfDayRecordsMap = groupRecordsByTimeOfDay(records);
Map<TimeOfDay, Double> timeOfDayAverageHappiness = calculateTimeOfDayAverageHappiness(timeOfDayRecordsMap);
Map<TimeOfDay, Integer> timeOfDayFrequency = calculateTimeOfDayFrequency(timeOfDayRecordsMap);

// ํ‰๊ท  ํ–‰๋ณต๋„์™€ ๋นˆ๋„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‹œ๊ฐ„๋Œ€๋“ค์„ ์ •๋ ฌ
List<TimeOfDay> sortedTimesOfDay = sortTimesOfDay(timeOfDayAverageHappiness, timeOfDayFrequency);

// ์ƒ์œ„ N๊ฐœ์˜ ์‹œ๊ฐ„๋Œ€ ์„ ์ •
for (int i = 0; i < sortedTimesOfDay.size(); i++) {
TimeOfDay timeOfDay = sortedTimesOfDay.get(i);
TimeOfDayHappinessDto timeOfDayDto = ReportConverter.toTimeOfDayHappinessDto(i + 1, timeOfDay);
timeOfDayRankings.add(timeOfDayDto);
}

return timeOfDayRankings;
}

private static Map<TimeOfDay, List<Record>> groupRecordsByTimeOfDay(List<Record> records) {
return records.stream()
.collect(Collectors.groupingBy(record -> TimeOfDay.of(record.getCreatedAt().getHour())));
}

private static Map<TimeOfDay, Double> calculateTimeOfDayAverageHappiness(Map<TimeOfDay, List<Record>> timeOfDayRecordsMap) {
Map<TimeOfDay, Double> timeOfDayAverageHappiness = new HashMap<>();
timeOfDayRecordsMap.forEach((timeOfDay, recordList) -> {
double averageHappiness = recordList.stream()
.mapToInt(Record::getHappiness)
.average()
.orElse(Double.NaN);
timeOfDayAverageHappiness.put(timeOfDay, averageHappiness);
});
return timeOfDayAverageHappiness;
}

private static Map<TimeOfDay, Integer> calculateTimeOfDayFrequency(Map<TimeOfDay, List<Record>> timeOfDayRecordsMap) {
Map<TimeOfDay, Integer> timeOfDayFrequency = new HashMap<>();
timeOfDayRecordsMap.forEach((timeOfDay, recordList) -> {
timeOfDayFrequency.put(timeOfDay, recordList.size());
});
return timeOfDayFrequency;
}

private static Optional<TimeOfDay> findHappiestTimeOfDay(Map<TimeOfDay, Double> timeOfDayAverageHappiness,
Map<TimeOfDay, Integer> timeOfDayFrequency,
double maxHappiness) {
return timeOfDayAverageHappiness.entrySet().stream()
.filter(entry -> entry.getValue() == maxHappiness)
.max(Comparator.comparing(entry -> timeOfDayFrequency.get(entry.getKey())))
.map(Map.Entry::getKey);
}

private static List<TimeOfDay> findTimesOfDayWithMaxHappiness(Map<TimeOfDay, Double> timeOfDayAverageHappiness,
double maxHappiness) {
return timeOfDayAverageHappiness.entrySet().stream()
.filter(entry -> entry.getValue() == maxHappiness)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}

private static List<TimeOfDay> sortTimesOfDay(Map<TimeOfDay, Double> timeOfDayAverageHappiness,
Map<TimeOfDay, Integer> timeOfDayFrequency) {
return timeOfDayAverageHappiness.entrySet().stream()
.sorted((entry1, entry2) -> {
int compare = Double.compare(entry2.getValue(), entry1.getValue());
if (compare == 0) {
compare = Integer.compare(timeOfDayFrequency.get(entry2.getKey()), timeOfDayFrequency.get(entry1.getKey()));
}
if (compare == 0) {
compare = entry1.getKey().name().compareTo(entry2.getKey().name());
}
return compare;
})
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package com.hobak.happinessql.domain.report.converter;

import com.hobak.happinessql.domain.report.domain.TimePeriod;
import com.hobak.happinessql.domain.report.dto.ActivityHappinessDto;
import com.hobak.happinessql.domain.report.dto.LocationHappinessDto;
import com.hobak.happinessql.domain.report.dto.ReportGraphResponseDto;
import com.hobak.happinessql.domain.report.dto.ReportSummaryResponseDto;
import com.hobak.happinessql.domain.report.domain.TimeOfDay;
import com.hobak.happinessql.domain.report.dto.*;

import java.util.ArrayList;

public class ReportConverter {
public static ReportSummaryResponseDto toReportSummaryResponseDto(TimePeriod timePeriod, String location, String activity) {
public static ReportSummaryResponseDto toReportSummaryResponseDto(TimeOfDay timeOfDay, String location, String activity) {
return ReportSummaryResponseDto.builder()
.activity(activity)
.location(location)
.timePeriod(timePeriod)
.timeOfDay(timeOfDay)
.build();
}

Expand All @@ -37,4 +34,11 @@ public static ReportGraphResponseDto toReportGraphResponseDto(ArrayList<String>
.happiness(happiness)
.build();
}

public static TimeOfDayHappinessDto toTimeOfDayHappinessDto(int ranking, TimeOfDay timeOfDay) {
return TimeOfDayHappinessDto.builder()
.ranking(ranking)
.timeOfDay(timeOfDay)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import lombok.AllArgsConstructor;

@AllArgsConstructor
public enum TimePeriod {
public enum TimeOfDay {
DAWN("์ƒˆ๋ฒฝ"),
MORNING("์•„์นจ"),
AFTERNOON("๋‚ฎ"),
Expand All @@ -14,7 +14,7 @@ public enum TimePeriod {

private final String viewName;

public static TimePeriod of(int hour) {
public static TimeOfDay of(int hour) {
if (hour >= 0 && hour < 5) {
return DAWN;
} else if (hour >= 5 && hour < 9) {
Expand All @@ -29,8 +29,8 @@ public static TimePeriod of(int hour) {
}

@JsonCreator
public static TimePeriod from(String value) {
for (TimePeriod status : TimePeriod.values()) {
public static TimeOfDay from(String value) {
for (TimeOfDay status : TimeOfDay.values()) {
if (status.getViewName().equals(value)) {
return status;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.hobak.happinessql.domain.report.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.hobak.happinessql.domain.report.domain.TimePeriod;
import com.hobak.happinessql.domain.report.domain.TimeOfDay;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -12,16 +12,16 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReportSummaryResponseDto {

@JsonProperty("time_period")
private TimePeriod timePeriod;
@JsonProperty("time_of_day")
private TimeOfDay timeOfDay;

private String location;

private String activity;

@Builder
public ReportSummaryResponseDto(TimePeriod timePeriod, String location, String activity) {
this.timePeriod = timePeriod;
public ReportSummaryResponseDto(TimeOfDay timeOfDay, String location, String activity) {
this.timeOfDay = timeOfDay;
this.location = location;
this.activity = activity;
}
Expand Down
Loading

0 comments on commit 803b799

Please sign in to comment.