Skip to content

Commit

Permalink
Merge pull request #63 from Memento-Makers/feature/map-api
Browse files Browse the repository at this point in the history
feat: 지역별 전체 사진 조회 API 개발 #62
  • Loading branch information
voka authored Nov 1, 2023
2 parents b916c26 + 56adefc commit 3c29694
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package mmm.emopic.app.domain.category.repository;

import com.querydsl.core.Tuple;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import mmm.emopic.app.domain.category.repository.CategoryRepositoryCustom;
import org.springframework.stereotype.Repository;

import java.util.List;

import static mmm.emopic.app.domain.category.QCategory.category;
import static mmm.emopic.app.domain.category.QPhotoCategory.photoCategory;

@Repository
Expand All @@ -18,13 +20,24 @@ public class CategoryRepositoryImpl implements CategoryRepositoryCustom {

@Override
public List<Tuple> getMostCategory(Long size) {
Long defaultId = queryFactory.select(category.id).from(category)
.where(eqDefault())
.fetchFirst();


List<Tuple> queryResults = queryFactory
.select(photoCategory.category.id, photoCategory.category.id.count())
.from(photoCategory)
.groupBy(photoCategory.category.id)
.having(photoCategory.category.id.ne(defaultId))
.orderBy(photoCategory.category.id.count().desc())
.limit(size)
.fetch();
return queryResults;
}

private BooleanExpression eqDefault(){
return category.name.eq("default");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,12 @@ public ResponseEntity<BaseResponse> getPhotoByCity(){
List<CityResponse> response = locationService.getPhotoByCity();
return ResponseEntity.ok(new BaseResponse( HttpStatus.OK.value(), "지역별 대표 사진 조회 완료", response));
}

@GetMapping("/locations/city/photos")
@Operation(summary = "지역의 전체 사진 조회", responses = {
@ApiResponse(responseCode = "200", description = "지역의 전체 사진 조회 완료", content = @Content(schema = @Schema(implementation = PageResponse.class)))
})
public ResponseEntity<BaseResponse> getAllPhotoOfCity(@Param("city") String city, @PageableDefault(page = 0, size = 20, sort="snapped_at",direction = Sort.Direction.DESC) Pageable pageable){
PageResponse response = locationService.getAllPhotosByCity(city,pageable);
return ResponseEntity.ok(new BaseResponse( HttpStatus.OK.value(), String.format("%s의 전체 사진 조회 완료",city), response));
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
package mmm.emopic.app.domain.location;

import lombok.RequiredArgsConstructor;
import mmm.emopic.app.domain.category.Category;
import mmm.emopic.app.domain.category.PhotoCategory;
import mmm.emopic.app.domain.category.repository.CategoryRepository;
import mmm.emopic.app.domain.category.repository.PhotoCategoryRepository;
import mmm.emopic.app.domain.emotion.Emotion;
import mmm.emopic.app.domain.emotion.PhotoEmotion;
import mmm.emopic.app.domain.emotion.repository.EmotionRepository;
import mmm.emopic.app.domain.emotion.repository.PhotoEmotionRepository;
import mmm.emopic.app.domain.location.dto.response.CityResponse;
import mmm.emopic.app.domain.location.dto.response.LocationPhotoResponse;
import mmm.emopic.app.domain.location.dto.response.LocationPointResponse;
import mmm.emopic.app.domain.location.dto.response.LocationRecentResponse;
import mmm.emopic.app.domain.photo.Photo;
import mmm.emopic.app.domain.photo.dto.response.KakaoCoord2regionResponse;
import mmm.emopic.app.domain.photo.dto.response.PageResponse;
import mmm.emopic.app.domain.photo.dto.response.PhotosInformationResponse;
import mmm.emopic.app.domain.photo.repository.PhotoRepositoryCustom;
import mmm.emopic.app.domain.photo.support.KakaoMapAPI;
import mmm.emopic.exception.ResourceNotFoundException;
import org.locationtech.jts.geom.Point;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class LocationService {

private final CategoryRepository categoryRepository;
private final EmotionRepository emotionRepository;
private final PhotoEmotionRepository photoEmotionRepository;
private final PhotoCategoryRepository photoCategoryRepository;
private final PhotoRepositoryCustom photoRepositoryCustom;

private final KakaoMapAPI kakaoMapAPI;
Expand Down Expand Up @@ -50,4 +68,27 @@ public List<CityResponse> getPhotoByCity() {
List<Photo> result = photoRepositoryCustom.findPhotosGroupByCity();
return result.stream().map(photo -> new CityResponse(photo)).collect(Collectors.toList());
}

public PageResponse getAllPhotosByCity(String city, Pageable pageable) {

Page<Photo> result = photoRepositoryCustom.findAllPhotosByCity(city,pageable);
List<PhotosInformationResponse> photosInformationResponseList = new ArrayList<>();
for(Photo photo: result) {
List<PhotoCategory> photoCategoryList = photoCategoryRepository.findByPhotoId(photo.getId());
List<Category> categories = new ArrayList<>();
for (PhotoCategory photoCategory : photoCategoryList) {
Long cid = photoCategory.getCategory().getId();
categories.add(categoryRepository.findById(cid).orElseThrow(() -> new ResourceNotFoundException("category", cid)));
}
List<PhotoEmotion> photoEmotionList = photoEmotionRepository.findByPhotoId(photo.getId());
List<Emotion> emotions = new ArrayList<>();
for (PhotoEmotion photoEmotion : photoEmotionList) {
Long eid = photoEmotion.getEmotion().getId();
emotions.add(emotionRepository.findById(eid).orElseThrow(() -> new ResourceNotFoundException("emotion", eid)));
}
photosInformationResponseList.add(new PhotosInformationResponse(photo,categories,emotions));
}
return new PageResponse(new PageImpl<>(photosInformationResponseList,result.getPageable(),result.getTotalElements()));

}
}
4 changes: 2 additions & 2 deletions emopic/src/main/java/mmm/emopic/app/domain/photo/Photo.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public Photo(Long id, String name, LocalDateTime snapped_at, String caption, Boo
this.location = location;
}

public void createSnappedAt(Date date){
this.snapped_at = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
public void createSnappedAt(LocalDateTime date){
this.snapped_at = date;
}

public void createLocation(Location location){
Expand Down
43 changes: 20 additions & 23 deletions emopic/src/main/java/mmm/emopic/app/domain/photo/PhotoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,40 +106,41 @@ public PhotoUploadResponse createPhoto(PhotoUploadRequest photoUploadRequest) {
boolean exists_gps_info = false;

Location location = null;
Optional<Point> point = null;


// 저장하기
Photo savedPhoto = photoRepository.save(photo);

KakaoCoord2regionResponse info = null;
if(metadata.isPresent()){
Optional<Point> point = metadataExtractor.getLocationPoint(metadata.get());
point = metadataExtractor.getLocationPoint(metadata.get());
if(point.isPresent()){ // GPS 정보 있으면 추출
Optional<KakaoCoord2regionResponse> LocationInfo = kakaoMapAPI.getLocationInfo(point.get().getX(),point.get().getY());
if(LocationInfo.isPresent()){
KakaoCoord2regionResponse info = LocationInfo.orElseThrow(() -> new RuntimeException("Kakao Map api 사용 도중 에러가 발생했습니다"));
info = LocationInfo.orElseThrow(() -> new RuntimeException("Kakao Map api 사용 도중 에러가 발생했습니다"));
exists_gps_info = true;
location = Location.builder()
.full_address(info.getAddress_name())
.address_1depth(info.getRegion_1depth_name())
.address_2depth(info.getRegion_2depth_name())
.address_3depth(info.getRegion_3depth_name())
.address_4depth(info.getRegion_4depth_name())
.latitude(point.get().getX())
.longitude(point.get().getY())
.photoId(savedPhoto.getId())
.build();
}
}
Optional<Date> snappedDate = metadataExtractor.getSnappedDate(metadata.get());
Optional<LocalDateTime> snappedDate = metadataExtractor.getSnappedDate(metadata.get());
if(snappedDate.isPresent()){ // 날짜 정보가 있으면 추출
photo.createSnappedAt(snappedDate.get());
}
else{
photo.createSnappedAt(new Date());
photo.createSnappedAt(LocalDateTime.now());
}
}

// 저장하기
Photo savedPhoto = photoRepository.save(photo);

if(exists_gps_info){//위치 정보가 있으면 저장
location = Location.builder()
.full_address(info.getAddress_name())
.address_1depth(info.getRegion_1depth_name())
.address_2depth(info.getRegion_2depth_name())
.address_3depth(info.getRegion_3depth_name())
.address_4depth(info.getRegion_4depth_name())
.latitude(point.get().getX())
.longitude(point.get().getY())
.photoId(savedPhoto.getId())
.build();
Location saved = locationRepository.save(location);
photo.createLocation(saved);
}
Expand All @@ -164,11 +165,7 @@ public void requestCategories(Photo photo){

CategoryInferenceResponse categoryInferenceResponse = photoInferenceWithAI.getClassificationsByPhoto(photo.getSignedUrl()).orElseThrow(() -> new RuntimeException("classification 과정에서 오류 발생"));

List<String> requiredTranslateResult = categoryInferenceResponse.getCategories();
List<String> result = new ArrayList<>();
for(String translateText :requiredTranslateResult){
result.add(translators.papagoTranslate(translateText));
}
List<String> result = categoryInferenceResponse.getCategories();
for(String categoryName : result){
Category category = categoryRepository.findByName(categoryName).orElseGet(() -> createCategory(categoryName));
PhotoCategory photoCategory = PhotoCategory.builder().photo(photo).category(category).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mmm.emopic.app.domain.photo.dto.response;

import lombok.Builder;
import lombok.Getter;
import mmm.emopic.app.domain.category.Category;
import mmm.emopic.app.domain.category.PhotoCategory;
Expand All @@ -24,6 +25,8 @@ public class PhotoInformationResponse {

private String signedUrl;

private String caption;

private String uploadDateTime;

private Long diaryId;
Expand All @@ -40,6 +43,7 @@ public PhotoInformationResponse(Photo photo, Diary diary, List<Category> categor
this.photoId = photo.getId();
this.signedUrl = photo.getSignedUrl();
this.diaryId = diary.getId();
this.caption = photo.getCaption();
Optional<String> optionDiaryContent = Optional.ofNullable(diary.getContent());
if(optionDiaryContent.isPresent()){
this.diaryContent = diary.getContent();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mmm.emopic.app.domain.photo.dto.response;

import lombok.Getter;
import mmm.emopic.app.domain.category.Category;
import mmm.emopic.app.domain.diary.Diary;
import mmm.emopic.app.domain.emotion.Emotion;
Expand All @@ -13,6 +14,7 @@
import java.util.Optional;
import java.util.stream.Collectors;

@Getter
public class PhotosInformationResponse {
private Long photoId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public interface PhotoRepositoryCustom {
Optional<Photo> findRecentPhoto();

List<Photo> findPhotosGroupByCity();

Page<Photo> findAllPhotosByCity(String city, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ public List<Photo> findPhotosGroupByCity() {
return result;
}

@Override
public Page<Photo> findAllPhotosByCity(String city, Pageable pageable) {

List<Photo> queryResults = queryFactory
.selectFrom(photo)
.where(
eqLocationCity(city)
)
.orderBy(makeOrder(pageable.getSort()))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPQLQuery<Photo> count = queryFactory
.select(photo)
.from(photo)
.where(
eqLocationCity(city)
);

return PageableExecutionUtils.getPage(queryResults,pageable,() -> count.fetchCount());
}

private OrderSpecifier[] makeOrder(Sort sort) {
List<OrderSpecifier> orders = new ArrayList<>();

Expand Down Expand Up @@ -178,5 +200,4 @@ private OrderSpecifier[] makeAscOrder(String properties){
private Sort makeSort(String properties, Sort.Direction direction){
return Sort.by(direction,properties);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Optional;
import java.util.TimeZone;

@Configuration
public class MetadataExtractor {

public Optional<Date> getSnappedDate(Metadata metadata){
public Optional<LocalDateTime> getSnappedDate(Metadata metadata){
if(metadata.containsDirectoryOfType(ExifIFD0Directory.class)){
ExifIFD0Directory exif = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
if(exif.containsTag(306)){
return Optional.of(exif.getDate(306));
LocalDateTime date = LocalDateTime.parse(exif.getString(306), DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"));
return Optional.of(date);
}
}
if(metadata.containsDirectoryOfType(ExifSubIFDDirectory.class)) {
ExifSubIFDDirectory subExif = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
if(subExif.containsTag(306)){
return Optional.of(subExif.getDate(306));
LocalDateTime date = LocalDateTime.parse(subExif.getString(306), DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"));
return Optional.of(date);
}
}
return Optional.empty();
Expand Down

0 comments on commit 3c29694

Please sign in to comment.