Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion pointtils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.pointtils.pointtils.src.application.mapper.InterpreterResponseMapper;
import com.pointtils.pointtils.src.application.mapper.LocationMapper;
import com.pointtils.pointtils.src.core.domain.entities.Interpreter;
import com.pointtils.pointtils.src.core.domain.entities.enums.DayOfWeek;
import com.pointtils.pointtils.src.core.domain.entities.enums.Gender;
import com.pointtils.pointtils.src.core.domain.entities.enums.InterpreterModality;
import com.pointtils.pointtils.src.core.domain.entities.enums.UserStatus;
Expand All @@ -24,7 +23,6 @@

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -77,16 +75,14 @@ public void delete(UUID id) {
repository.save(interpreter);
}

public List<InterpreterListResponseDTO> findAll(
String modality,
String gender,
String city,
String uf,
String neighborhood,
String specialty,
String availableDate,
String name) {

public List<InterpreterListResponseDTO> findAll(String modality,
String gender,
String city,
String uf,
String neighborhood,
String specialty,
String availableDate,
String name) {
InterpreterModality modalityEnum = null;
if (modality != null) {
modalityEnum = InterpreterModality.valueOf(modality.toUpperCase());
Expand All @@ -97,16 +93,9 @@ public List<InterpreterListResponseDTO> findAll(
genderEnum = Gender.valueOf(gender.toUpperCase());
}

DayOfWeek dayOfWeek = null;
LocalTime requestedStart = null;
LocalTime requestedEnd = null;

LocalDateTime dateTime = null;
if (availableDate != null) {
LocalDateTime dateTime = LocalDateTime.parse(availableDate,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
dayOfWeek = DayOfWeek.valueOf(dateTime.getDayOfWeek().name().substring(0, 3));
requestedStart = dateTime.toLocalTime();
requestedEnd = requestedStart.plusHours(1);
dateTime = LocalDateTime.parse(availableDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
}

List<UUID> specialtyList = null;
Expand All @@ -116,9 +105,8 @@ public List<InterpreterListResponseDTO> findAll(
.toList();
}

return repository.findAll(
InterpreterSpecification.filter(modalityEnum, uf, city, neighborhood, specialtyList, genderEnum, dayOfWeek,
requestedStart, requestedEnd, name))
return repository.findAll(InterpreterSpecification.filter(modalityEnum, uf, city, neighborhood, specialtyList,
genderEnum, dateTime, name))
.stream()
.map(responseMapper::toListResponseDTO)
.toList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.pointtils.pointtils.src.infrastructure.repositories.spec;

import com.pointtils.pointtils.src.core.domain.entities.Appointment;
import com.pointtils.pointtils.src.core.domain.entities.Interpreter;
import com.pointtils.pointtils.src.core.domain.entities.Location;
import com.pointtils.pointtils.src.core.domain.entities.Schedule;
import com.pointtils.pointtils.src.core.domain.entities.UserSpecialty;
import com.pointtils.pointtils.src.core.domain.entities.enums.AppointmentStatus;
import com.pointtils.pointtils.src.core.domain.entities.enums.DayOfWeek;
import com.pointtils.pointtils.src.core.domain.entities.enums.Gender;
import com.pointtils.pointtils.src.core.domain.entities.enums.InterpreterModality;
Expand All @@ -13,17 +15,24 @@
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import lombok.experimental.UtilityClass;
import org.springframework.data.jpa.domain.Specification;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@UtilityClass
public class InterpreterSpecification {

private static final String START_TIME_FIELD = "startTime";
private static final String END_TIME_FIELD = "endTime";

@SuppressWarnings("null")
public static Specification<Interpreter> filter(
InterpreterModality modality,
Expand All @@ -32,50 +41,139 @@ public static Specification<Interpreter> filter(
String neighborhood,
List<UUID> specialties,
Gender gender,
DayOfWeek dayOfWeek,
LocalTime requestedStart,
LocalTime requestedEnd,
LocalDateTime availableDate,
String name
) {
return (root, query, cb) -> {
query.distinct(true);
Join<Interpreter, Location> location = root.join("locations", JoinType.LEFT);
Join<Interpreter, Schedule> schedule = root.join("schedules", JoinType.LEFT);

Predicate predicate = cb.conjunction();

if (modality == InterpreterModality.ALL) {
predicate = cb.and(predicate, root.get("modality").in(InterpreterModality.ALL, InterpreterModality.ONLINE, InterpreterModality.PERSONALLY));
} else if (modality != null) {
predicate = cb.and(predicate, root.get("modality").in(InterpreterModality.ALL, modality));
}
if (name != null) predicate = cb.and(predicate, cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%"));
if (gender != null) predicate = cb.and(predicate, cb.equal(root.get("gender"), gender));
if (uf != null) predicate = cb.and(predicate, cb.equal(location.get("uf"), uf));
if (city != null) predicate = cb.and(predicate, cb.like(cb.lower(location.get("city")), "%" + city.toLowerCase() + "%"));
if (neighborhood != null) predicate = cb.and(predicate, cb.like(cb.lower(location.get("neighborhood")), "%" + neighborhood.toLowerCase() + "%"));
if (!Collections.isEmpty(specialties)) {
Subquery<UUID> subquery = buildSpecialtiesSubquery(query, specialties, cb);
predicate = cb.and(predicate, root.get("id").in(subquery));
if (name != null) {
predicate = cb.and(predicate, cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%"));
}
if (dayOfWeek != null && requestedStart != null && requestedEnd != null) {
predicate = cb.and(predicate, cb.equal(schedule.get("day"), dayOfWeek));
predicate = cb.and(predicate, cb.lessThanOrEqualTo(schedule.get("startTime"), requestedStart));
predicate = cb.and(predicate, cb.greaterThanOrEqualTo(schedule.get("endTime"), requestedEnd));
if (gender != null) {
predicate = cb.and(predicate, cb.equal(root.get("gender"), gender));
}

predicate = checkIfInterpreterHasModality(predicate, cb, root, modality);
predicate = checkIfInterpreterHasLocation(predicate, cb, root, uf, city, neighborhood);
predicate = checkIfInterpreterHasSpecialties(predicate, query, cb, root, specialties);
predicate = checkIfInterpreterIsAvailableOnRequestedDate(predicate, query, cb, root, availableDate);
return predicate;
};
}

private static Subquery<UUID> buildSpecialtiesSubquery(CriteriaQuery<?> query, List<UUID> specialties,
CriteriaBuilder criteriaBuilder) {
private static Predicate checkIfInterpreterHasModality(Predicate predicate,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
InterpreterModality modality) {
if (modality == InterpreterModality.ALL) {
predicate = criteriaBuilder.and(predicate, root.get("modality")
.in(InterpreterModality.ALL, InterpreterModality.ONLINE, InterpreterModality.PERSONALLY));
} else if (Objects.nonNull(modality)) {
predicate = criteriaBuilder.and(predicate, root.get("modality").in(InterpreterModality.ALL, modality));
}
return predicate;
}

private static Predicate checkIfInterpreterHasLocation(Predicate predicate,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
String uf,
String city,
String neighborhood) {

Join<Interpreter, Location> location = root.join("locations", JoinType.LEFT);
if (Objects.nonNull(uf)) {
predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(location.get("uf"), uf));
}
if (Objects.nonNull(city)) {
predicate = criteriaBuilder.and(predicate, criteriaBuilder
.like(criteriaBuilder.lower(location.get("city")), "%" + city.toLowerCase() + "%"));
}
if (Objects.nonNull(neighborhood)) {
predicate = criteriaBuilder.and(predicate, criteriaBuilder
.like(criteriaBuilder.lower(location.get("neighborhood")), "%" + neighborhood.toLowerCase() + "%"));
}
return predicate;
}

private static Predicate checkIfInterpreterHasSpecialties(Predicate predicate,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
List<UUID> specialties) {
if (Collections.isEmpty(specialties)) {
return predicate;
}
Subquery<UUID> subquery = query.subquery(UUID.class);
var subRoot = subquery.from(UserSpecialty.class);
subquery.select(subRoot.get("user").get("id"))
.where(subRoot.get("specialty").get("id").in(specialties))
.groupBy(subRoot.get("user").get("id"))
.having(criteriaBuilder.equal(criteriaBuilder.countDistinct(subRoot.get("specialty").get("id")), specialties.size()));
return subquery;
return criteriaBuilder.and(predicate, root.get("id").in(subquery));
}

private static Predicate checkIfInterpreterIsAvailableOnRequestedDate(Predicate predicate,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
LocalDateTime availableDate) {
if (Objects.isNull(availableDate)) {
return predicate;
}

LocalDate requestedDate = availableDate.toLocalDate();
DayOfWeek dayOfWeek = DayOfWeek.valueOf(availableDate.getDayOfWeek().name().substring(0, 3));
LocalTime requestedStart = availableDate.toLocalTime();
LocalTime requestedEnd = requestedStart.plusHours(1);

predicate = checkIfInterpreterHasScheduleOnRequestedDate(predicate, criteriaBuilder, root, dayOfWeek, requestedStart, requestedEnd);
return checkIfInterpreterHasNoAppointmentOnRequestDate(predicate, query, criteriaBuilder, root, requestedDate, requestedStart, requestedEnd);
}

private static Predicate checkIfInterpreterHasScheduleOnRequestedDate(Predicate predicate,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
DayOfWeek dayOfWeek,
LocalTime requestedStart,
LocalTime requestedEnd) {
Join<Interpreter, Schedule> schedule = root.join("schedules", JoinType.LEFT);
predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(schedule.get("day"), dayOfWeek));
predicate = criteriaBuilder.and(predicate, criteriaBuilder
.lessThanOrEqualTo(schedule.get(START_TIME_FIELD), requestedStart));
return criteriaBuilder.and(predicate, criteriaBuilder
.greaterThanOrEqualTo(schedule.get(END_TIME_FIELD), requestedEnd));
}

private static Predicate checkIfInterpreterHasNoAppointmentOnRequestDate(Predicate predicate,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder,
Root<Interpreter> root,
LocalDate requestedDate,
LocalTime requestedStart,
LocalTime requestedEnd) {
Subquery<UUID> subquery = query.subquery(UUID.class);
var subRoot = subquery.from(Appointment.class);
subquery.select(subRoot.get("interpreter").get("id"))
.where(criteriaBuilder.and(
subRoot.get("status").in(AppointmentStatus.ACCEPTED, AppointmentStatus.COMPLETED),
criteriaBuilder.equal(subRoot.get("date"), requestedDate),
criteriaBuilder.or(
criteriaBuilder.and(
criteriaBuilder.greaterThan(subRoot.get(START_TIME_FIELD), requestedStart),
criteriaBuilder.lessThan(subRoot.get(START_TIME_FIELD), requestedEnd)
),
criteriaBuilder.and(
criteriaBuilder.greaterThan(subRoot.get(END_TIME_FIELD), requestedStart),
criteriaBuilder.lessThan(subRoot.get(END_TIME_FIELD), requestedEnd)
),
criteriaBuilder.and(
criteriaBuilder.lessThanOrEqualTo(subRoot.get(START_TIME_FIELD), requestedStart),
criteriaBuilder.greaterThanOrEqualTo(subRoot.get(END_TIME_FIELD), requestedEnd)
)
)
));
return criteriaBuilder.and(predicate, criteriaBuilder.not(root.get("id").in(subquery)));
}
}
Loading
Loading