package pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import pt.ulisboa.tecnico.socialsoftware.tutor.exceptions.TutorException;
import pt.ulisboa.tecnico.socialsoftware.tutor.execution.domain.CourseExecution;
import pt.ulisboa.tecnico.socialsoftware.tutor.execution.repository.CourseExecutionRepository;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.domain.TeacherDashboard;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.domain.QuestionStats;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.dto.TeacherDashboardDto;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.dto.QuestionStatsDto;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.repository.TeacherDashboardRepository;
import pt.ulisboa.tecnico.socialsoftware.tutor.teacherdashboard.repository.QuestionStatsRepository;
import pt.ulisboa.tecnico.socialsoftware.tutor.user.domain.Teacher;
import pt.ulisboa.tecnico.socialsoftware.tutor.user.repository.TeacherRepository;
import pt.ulisboa.tecnico.socialsoftware.tutor.question.domain.Course;
import pt.ulisboa.tecnico.socialsoftware.tutor.exceptions.ErrorMessage;
import java.util.stream.Collectors;
import java.util.*;
import java.util.stream.*;
import static pt.ulisboa.tecnico.socialsoftware.tutor.exceptions.ErrorMessage.*;
@Service
public class TeacherDashboardService {
@Autowired
private CourseExecutionRepository courseExecutionRepository;
@Autowired
private TeacherRepository teacherRepository;
@Autowired
private TeacherDashboardRepository teacherDashboardRepository;
@Autowired
private QuestionStatsRepository questionStatsRepository;
@Transactional(isolation = Isolation.READ_COMMITTED)
public TeacherDashboardDto getTeacherDashboard(int courseExecutionId, int teacherId) {
CourseExecution courseExecution = courseExecutionRepository.findById(courseExecutionId)
.orElseThrow(() -> new TutorException(COURSE_EXECUTION_NOT_FOUND));
Teacher teacher = teacherRepository.findById(teacherId)
.orElseThrow(() -> new TutorException(USER_NOT_FOUND, teacherId));
if (!teacher.getCourseExecutions().contains(courseExecution))
throw new TutorException(TEACHER_NO_COURSE_EXECUTION);
Optional<TeacherDashboard> dashboardOptional = teacher.getDashboards().stream()
.filter(dashboard -> dashboard.getCourseExecution().getId().equals(courseExecutionId))
.findAny();
return dashboardOptional.
map(TeacherDashboardDto::new).
orElseGet(() -> createAndReturnTeacherDashboardDto(courseExecution, teacher));
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public TeacherDashboardDto createTeacherDashboard(int courseExecutionId, int teacherId) {
CourseExecution courseExecution = courseExecutionRepository.findById(courseExecutionId)
.orElseThrow(() -> new TutorException(COURSE_EXECUTION_NOT_FOUND));
Teacher teacher = teacherRepository.findById(teacherId)
.orElseThrow(() -> new TutorException(USER_NOT_FOUND, teacherId));
if (teacher.getDashboards().stream().anyMatch(dashboard -> dashboard.getCourseExecution().equals(courseExecution)))
throw new TutorException(TEACHER_ALREADY_HAS_DASHBOARD);
if (!teacher.getCourseExecutions().contains(courseExecution))
throw new TutorException(TEACHER_NO_COURSE_EXECUTION);
return createAndReturnTeacherDashboardDto(courseExecution, teacher);
}
private TeacherDashboardDto createAndReturnTeacherDashboardDto(CourseExecution courseExecution, Teacher teacher) {
TeacherDashboard teacherDashboard = new TeacherDashboard(courseExecution, teacher);
teacherDashboardRepository.save(teacherDashboard);
Course course = courseExecution.getCourse();
List<CourseExecution> recentCourseExecutions = this.getRecentCourseExecutions(course);
List<QuestionStats> questionStatsList = new ArrayList<QuestionStats>();
int recentCourseExecutionsSize = recentCourseExecutions.size();
for(int i = 0; i < recentCourseExecutionsSize; i++){
QuestionStats questionStats = new QuestionStats(teacherDashboard, recentCourseExecutions.get(i));
questionStatsRepository.save(questionStats);
questionStatsList.add(questionStats);
}
teacherDashboard.setQuestionStats(questionStatsList);
teacherDashboard.update();
return new TeacherDashboardDto(teacherDashboard);
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public void removeTeacherDashboard(Integer dashboardId) {
if (dashboardId == null)
throw new TutorException(DASHBOARD_NOT_FOUND, -1);
TeacherDashboard teacherDashboard = teacherDashboardRepository.findById(dashboardId).orElseThrow(() -> new TutorException(DASHBOARD_NOT_FOUND, dashboardId));
List<QuestionStats> questionStats = teacherDashboard.getQuestionStats();
int questionStatsListSize= questionStats.size();
for (int i = 0; i < questionStatsListSize; i++){
questionStatsRepository.delete(questionStats.get(i));
}
teacherDashboard.remove();
teacherDashboardRepository.delete(teacherDashboard);
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateTeacherDashboard(Integer dashboardId) {
if (dashboardId == null)
throw new TutorException(DASHBOARD_NOT_FOUND, -1);
TeacherDashboard teacherDashboard = teacherDashboardRepository.findById(dashboardId).orElseThrow(() -> new TutorException(DASHBOARD_NOT_FOUND));
teacherDashboard.update();
}
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAllTeacherDashboards(){
List<Teacher> teachers = StreamSupport.stream(teacherRepository.findAll().spliterator(), false).collect(Collectors.toList());
for(Teacher teacher : teachers){
for(CourseExecution courseExecution : teacher.getCourseExecutions()){
if (teacher.getCourseExecutionDashboard(courseExecution) == null){
createTeacherDashboard(courseExecution.getId(), teacher.getId());
}
}
}
List<TeacherDashboard> listOfAllTeacherDashboards = StreamSupport.stream(teacherDashboardRepository.findAll().spliterator(), false).collect(Collectors.toList());
for(TeacherDashboard teacherDashboard : listOfAllTeacherDashboards){
updateTeacherDashboard(teacherDashboard.getId());
}
}
private List<CourseExecution> getRecentCourseExecutions(Course course){
Set<CourseExecution> courseExecutions = course.getCourseExecutions();
List<CourseExecution> recentCourseExecutions = courseExecutions.stream()
.filter(unfilteredCourseExecution -> {
try{
unfilteredCourseExecution.getEndDate();
} catch(IllegalStateException e){
return false;
}
return true;
})
.sorted(Comparator.comparing(CourseExecution::getEndDate).reversed())
.limit(3)
.collect(Collectors.toList());
return recentCourseExecutions;
}
}