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
1 change: 1 addition & 0 deletions .github/workflows/CI-CD-Test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
echo "Ejecutando test: Dado que no hay ninguna reserva registrada, Cuándo lo creo a nivel de servicio, Entonces la creación será exitosa."
echo "Ejecutando test: Dado que tengo 1 reserva registrada, Cuándo la elimino a nivel de servicio, Entonces la eliminación será exitosa."
echo "Ejecutando test: Dado que tengo 1 reserva registrada, Cuándo la elimino y consulto a nivel de servicio, Entonces el resultado de la consulta no retornará ningún resultado."

deploy:
name: Deploy
needs: test
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ build/
.env
errorLog.txt
requirements.pdf
4_Maven Verify.txt
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@
<artifactId>dotenv-java</artifactId>
<version>2.3.1</version>
</dependency>


</dependencies>

<dependencyManagement>
Expand Down
1,001 changes: 535 additions & 466 deletions src/main/java/edu/eci/cvds/prometeo/controller/UserController.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ Optional<GymSession> findBySessionDateAndStartTimeLessThanEqualAndEndTimeGreater
* Find sessions by date
*/
List<GymSession> findBySessionDate(LocalDate date);

/**
* Find sessions by trainer ID and date range
*/
List<GymSession> findByTrainerIdAndSessionDateBetween(UUID trainerId, LocalDate startDate, LocalDate endDate);
}
97 changes: 65 additions & 32 deletions src/main/java/edu/eci/cvds/prometeo/service/GymSessionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,121 @@

/**
* Service for managing gym sessions and time slots
* Note: This service requires GymSession and TimeSlot entities that don't appear in the provided code.
* Note: This service requires GymSession and TimeSlot entities that don't
* appear in the provided code.
* Implementation would need to create these entities.
*/
public interface GymSessionService {

/**
* Creates a new gym session
* @param date Date of the session
* @param startTime Start time
* @param endTime End time
* @param capacity Maximum capacity
*
* @param date Date of the session
* @param startTime Start time
* @param endTime End time
* @param capacity Maximum capacity
* @param description Optional description
* @param trainerId ID of the trainer creating the session
* @param trainerId ID of the trainer creating the session
* @return ID of the created session
*/
UUID createSession(LocalDate date, LocalTime startTime, LocalTime endTime,
int capacity, Optional<String> description, UUID trainerId);
UUID createSession(LocalDate date, LocalTime startTime, LocalTime endTime,
int capacity, Optional<String> description, UUID trainerId);

/**
* Updates an existing gym session
*
* @param sessionId ID of the session
* @param date New date
* @param date New date
* @param startTime New start time
* @param endTime New end time
* @param capacity New capacity
* @param endTime New end time
* @param capacity New capacity
* @param trainerId ID of the trainer making the update
* @return true if successfully updated
*/
boolean updateSession(UUID sessionId, LocalDate date, LocalTime startTime,
LocalTime endTime, int capacity, UUID trainerId);
boolean updateSession(UUID sessionId, LocalDate date, LocalTime startTime,
LocalTime endTime, int capacity, UUID trainerId);

/**
* Cancels a gym session
*
* @param sessionId ID of the session
* @param reason Reason for cancellation
* @param reason Reason for cancellation
* @param trainerId ID of the trainer canceling the session
* @return true if successfully canceled
*/
boolean cancelSession(UUID sessionId, String reason, UUID trainerId);

/**
* Gets all sessions for a specific date
*
* @param date Date to query
* @return List of sessions on that date
*/
List<Object> getSessionsByDate(LocalDate date);

/**
* Gets sessions created by a specific trainer
*
* @param trainerId ID of the trainer
* @return List of sessions by the trainer
*/
List<Object> getSessionsByTrainer(UUID trainerId);

/**
* Gets available time slots for a date
*
* @param date Date to check
* @return List of available time slots
*/
List<Map<String, Object>> getAvailableTimeSlots(LocalDate date);

/**
* Configures recurring sessions
* @param dayOfWeek Day of the week (1-7, where 1 is Monday)
* @param startTime Start time
* @param endTime End time
* @param capacity Maximum capacity
*
* @param dayOfWeek Day of the week (1-7, where 1 is Monday)
* @param startTime Start time
* @param endTime End time
* @param capacity Maximum capacity
* @param description Optional description
* @param trainerId ID of the trainer
* @param startDate Start date for recurrence
* @param endDate End date for recurrence
* @param trainerId ID of the trainer
* @param startDate Start date for recurrence
* @param endDate End date for recurrence
* @return Number of sessions created
*/
int configureRecurringSessions(int dayOfWeek, LocalTime startTime, LocalTime endTime,
int capacity, Optional<String> description, UUID trainerId,
LocalDate startDate, LocalDate endDate);
int capacity, Optional<String> description, UUID trainerId,
LocalDate startDate, LocalDate endDate);

/**
* Gets occupancy statistics for the gym
*
* @param startDate Start date
* @param endDate End date
* @param endDate End date
* @return Map of dates to occupancy percentages
*/
Map<LocalDate, Integer> getOccupancyStatistics(LocalDate startDate, LocalDate endDate);

/**
* Gets all students registered for a specific session
*
* @param sessionId ID of the session
* @return List of registered students with their details
*/
List<Map<String, Object>> getRegisteredStudentsForSession(UUID sessionId);

/**
* Gets attendance statistics for a trainer's sessions
* @param trainerId ID of the trainer
* @param startDate Start date for the period
* @param endDate End date for the period
* @return Map with attendance statistics
*/
Map<String, Object> getTrainerAttendanceStatistics(UUID trainerId, LocalDate startDate, LocalDate endDate);

/**
* Gets a specific gym session by its ID
* @param sessionId ID of the session to retrieve
* @return Session details
*/
Object getSessionById(UUID sessionId);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package edu.eci.cvds.prometeo.service.impl;

import edu.eci.cvds.prometeo.model.GymSession;
import edu.eci.cvds.prometeo.model.Reservation;
import edu.eci.cvds.prometeo.model.User;
import edu.eci.cvds.prometeo.repository.GymSessionRepository;
import edu.eci.cvds.prometeo.repository.ReservationRepository;
import edu.eci.cvds.prometeo.repository.UserRepository;
import edu.eci.cvds.prometeo.service.GymSessionService;
import edu.eci.cvds.prometeo.PrometeoExceptions;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -15,16 +19,22 @@
@Service
public class GymSessionServiceImpl implements GymSessionService {

private final GymSessionRepository gymSessionRepository;

@Autowired
public GymSessionServiceImpl(GymSessionRepository gymSessionRepository) {
this.gymSessionRepository = gymSessionRepository;
}
private GymSessionRepository gymSessionRepository;
@Autowired
private ReservationRepository reservationRepository;
@Autowired
private UserRepository userRepository;

// @Autowired
// public GymSessionServiceImpl(GymSessionRepository gymSessionRepository) {
// this.gymSessionRepository = gymSessionRepository;
// }

@Override
@Transactional
public UUID createSession(LocalDate date, LocalTime startTime, LocalTime endTime, int capacity, Optional<String> description, UUID trainerId) {
public UUID createSession(LocalDate date, LocalTime startTime, LocalTime endTime, int capacity,
Optional<String> description, UUID trainerId) {
// Prevent overlapping sessions
Optional<GymSession> overlapping = gymSessionRepository
.findBySessionDateAndStartTimeLessThanEqualAndEndTimeGreaterThanEqual(date, startTime, endTime);
Expand All @@ -44,7 +54,8 @@ public UUID createSession(LocalDate date, LocalTime startTime, LocalTime endTime

@Override
@Transactional
public boolean updateSession(UUID sessionId, LocalDate date, LocalTime startTime, LocalTime endTime, int capacity, UUID trainerId) {
public boolean updateSession(UUID sessionId, LocalDate date, LocalTime startTime, LocalTime endTime, int capacity,
UUID trainerId) {
GymSession session = gymSessionRepository.findById(sessionId)
.orElseThrow(() -> new PrometeoExceptions(PrometeoExceptions.SESION_NO_ENCONTRADA));
// Prevent overlapping with other sessions
Expand Down Expand Up @@ -130,7 +141,8 @@ public List<Map<String, Object>> getAvailableTimeSlots(LocalDate date) {

@Override
@Transactional
public int configureRecurringSessions(int dayOfWeek, LocalTime startTime, LocalTime endTime, int capacity, Optional<String> description, UUID trainerId, LocalDate startDate, LocalDate endDate) {
public int configureRecurringSessions(int dayOfWeek, LocalTime startTime, LocalTime endTime, int capacity,
Optional<String> description, UUID trainerId, LocalDate startDate, LocalDate endDate) {
int count = 0;
LocalDate date = startDate;
while (!date.isAfter(endDate)) {
Expand Down Expand Up @@ -176,4 +188,111 @@ public Map<LocalDate, Integer> getOccupancyStatistics(LocalDate startDate, Local
}
return stats;
}

@Override
public List<Map<String, Object>> getRegisteredStudentsForSession(UUID sessionId) {
// Verificar que existe la sesión
GymSession session = gymSessionRepository.findById(sessionId)
.orElseThrow(() -> new PrometeoExceptions(PrometeoExceptions.SESION_NO_ENCONTRADA));

// Buscar todas las reservas para esa sesión
List<Reservation> reservations = reservationRepository.findBySessionId(sessionId);

// Transformar los resultados
List<Map<String, Object>> result = new ArrayList<>();
for (Reservation reservation : reservations) {
// Obtener el usuario
User user = userRepository.findById(reservation.getUserId())
.orElse(null);

if (user != null) {
Map<String, Object> studentInfo = new HashMap<>();
studentInfo.put("reservationId", reservation.getId());
studentInfo.put("userId", user.getId());
studentInfo.put("name", user.getName());
studentInfo.put("institutionalId", user.getInstitutionalId());
studentInfo.put("status", reservation.getStatus());
studentInfo.put("attended", reservation.getAttended());
studentInfo.put("attendanceTime", reservation.getAttendanceTime());

result.add(studentInfo);
}
}

return result;
}

@Override
public Map<String, Object> getTrainerAttendanceStatistics(UUID trainerId, LocalDate startDate, LocalDate endDate) {
// Buscar todas las sesiones del entrenador en el periodo
List<GymSession> sessions = gymSessionRepository.findByTrainerIdAndSessionDateBetween(
trainerId, startDate, endDate);

int totalSessions = sessions.size();
int totalCapacity = 0;
int totalReservations = 0;
int totalAttendance = 0;

Map<LocalDate, Integer> dailyAttendance = new HashMap<>();

for (GymSession session : sessions) {
totalCapacity += session.getCapacity();
totalReservations += session.getReservedSpots();

// Buscar reservas para la sesión
List<Reservation> reservations = reservationRepository.findBySessionId(session.getId());
int sessionAttendance = (int) reservations.stream()
.filter(r -> r.getAttended())
.count();

totalAttendance += sessionAttendance;

// Agregar a estadísticas diarias
dailyAttendance.compute(session.getSessionDate(),
(date, count) -> (count == null) ? sessionAttendance : count + sessionAttendance);
}

// Calcular porcentajes
double occupancyRate = totalCapacity > 0 ? (double) totalReservations / totalCapacity * 100 : 0;
double attendanceRate = totalReservations > 0 ? (double) totalAttendance / totalReservations * 100 : 0;

Map<String, Object> statistics = new HashMap<>();
statistics.put("totalSessions", totalSessions);
statistics.put("totalCapacity", totalCapacity);
statistics.put("totalReservations", totalReservations);
statistics.put("totalAttendance", totalAttendance);
statistics.put("occupancyRate", Math.round(occupancyRate * 100) / 100.0); // Redondear a 2 decimales
statistics.put("attendanceRate", Math.round(attendanceRate * 100) / 100.0); // Redondear a 2 decimales
statistics.put("dailyAttendance", dailyAttendance);

return statistics;
}

@Override
public Object getSessionById(UUID sessionId) {
GymSession session = gymSessionRepository.findById(sessionId)
.orElseThrow(() -> new PrometeoExceptions(PrometeoExceptions.SESION_NO_ENCONTRADA));

// Obtener información adicional del entrenador
User trainer = userRepository.findById(session.getTrainerId())
.orElse(null);

Map<String, Object> sessionDetails = new HashMap<>();
sessionDetails.put("id", session.getId());
sessionDetails.put("date", session.getSessionDate());
sessionDetails.put("startTime", session.getStartTime());
sessionDetails.put("endTime", session.getEndTime());
sessionDetails.put("capacity", session.getCapacity());
sessionDetails.put("reservedSpots", session.getReservedSpots());

if (trainer != null) {
Map<String, Object> trainerInfo = new HashMap<>();
trainerInfo.put("id", trainer.getId());
trainerInfo.put("name", trainer.getName());
sessionDetails.put("trainer", trainerInfo);
}


return sessionDetails;
}
}
4 changes: 2 additions & 2 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ openai.api.url=${OPEN_AI_MODEL}
spring.datasource.hikari.properties.ssl=true
spring.datasource.hikari.properties.sslfactory=org.postgresql.ssl.NonValidatingFactory

# Server configuration
server.port=8081
# Server configuration, comentado porque no es necesario.
# server.port=8081
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import edu.eci.cvds.prometeo.PrometeoApplication; // Añade este import

@SpringBootTest(classes = PrometeoApplication.class)
@ActiveProfiles("test")
Expand Down