UserService.java

/**
 * TFG 75.678 - TFG Desarrollo web 2020 e-Learning for Schools
 * Copyright (C) 2020  Eduardo Rodriguez Carro
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.uoc.tfg.sel.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.uoc.tfg.sel.repository.EventJobRepository;
import org.uoc.tfg.sel.repository.UserRepository;
import org.uoc.tfg.sel.repository.UserTypeRepository;
import org.uoc.tfg.sel.repository.model.EventJob;
import org.uoc.tfg.sel.repository.model.User;
import org.uoc.tfg.sel.repository.model.UserType;
import org.uoc.tfg.sel.validation.ValidationException;
import org.uoc.tfg.sel.validation.ValidatorUtils;
import org.uoc.tfg.sel.web.model.ErrorCodes;
import org.uoc.tfg.sel.web.model.ModelUtils;

/**
 * Servicio de usuarios.
 *
 * @author Eduardo Rodriguez Carro
 */
@Service
public class UserService {
	
	/** The Constant logger. */
	private static final Logger logger = LoggerFactory.getLogger(UserService.class);
	
	/** The specialist type. */
	@Value("${user.type.specialist:3}")
	private Integer specialistType;
	
	/** The tutor type. */
	@Value("${user.type.tutor:2}")
	private Integer tutorType;
	
	/** The student type. */
	@Value("${user.type.student:5}")
	private Integer studentType;
	
	/** The student type. */
	@Value("${user.type.legalTutor:4}")
	private Integer legalTutorType;
	
	/** The user repository. */
	@Autowired
	private UserRepository userRepository;

	/** The user type repository. */
	@Autowired
	private UserTypeRepository userTypeRepository;

	@Autowired
	private EventJobRepository eventJobRepository;

	/**
	 * Gets the user all.
	 *
	 * @return the user all
	 */
	@Transactional(readOnly = true)
	public List<User> getUserAll() {
		Iterable<User> usersIt = userRepository.findAll();
		List<User> users = new ArrayList<>();
		usersIt.forEach(u-> {users.add(u);});
		return users;
	}
	
	/**
	 * Gets the user by id.
	 *
	 * @param id the id
	 * @return the user by id
	 */
	@Cacheable( value = "userCache",key = "#id",  unless = "#result == null")
	@Transactional(readOnly = true)
	public User getUserById(Integer id) {
		Optional<User> user = userRepository.findById(id);
		if (user.isPresent()) {
			return user.get();
		}
		return null;
	}
	
	/**
	 * Gets the user students.
	 *
	 * @return the user students
	 */
	@Transactional(readOnly = true)
	public List<User> getUserStudents() {
		UserType type = new UserType();
		type.setId(studentType);
		List<User> users = userRepository.findByTypeAndActive(type,true);	
		for(User user: users) {
			if (user.getLegalTutor() != null) {
				 logger.debug("User {} is assigned at {}",user.getId(), user.getLegalTutor().getId());
			}
		}
		return users;
	}
	
	/**
	 * Gets the user tutor.
	 *
	 * @return the user tutor
	 */
	@Transactional(readOnly = true)
	public List<User> getUserTutor() {
		UserType type = new UserType();
		type.setId(tutorType);
		return userRepository.findByTypeAndActive(type,true);
	}
	
	/**
	 * Gets the user teachers.
	 *
	 * @return the user teachers
	 */
	@Transactional(readOnly = true)
	public List<User> getUserTeachers() {
		Iterable<User> users1 = userRepository.findByTypeAndActive(new UserType(tutorType),true);
		Iterable<User> users2 = userRepository.findByTypeAndActive(new UserType(specialistType),true);		
		List<User> users = new ArrayList<>();		
		users1.forEach( (u) -> {users.add(u);});
		users2.forEach( (u) -> {users.add(u);});
		return users;
	}
	
	/**
	 * Gets the user types.
	 *
	 * @return the user types
	 */
	@Cacheable( value = "userTypeCache",  unless = "#result == null")
	@Transactional(readOnly = true)
	public List<UserType> getUserTypes() {
		return ServiceUtils.iterableToList(userTypeRepository.findAll());
	}
	
	/**
	 * Save.
	 *
	 * @param user the user
	 * @return the user
	 */
	@Transactional(readOnly = false)
	public User save(User user) {
		
		// Si es nuevo debe tener password y tipo
		if(user.getId() == null) {
			ValidatorUtils.assertIsNonNull("User password",user.getPassword());
			ValidatorUtils.assertIsNonNull("UserType",user.getType());
			ValidatorUtils.assertIsNonNull("UserType id",user.getType().getId());
		}
		
		// Si tiene tipo este tiene que existir
		if ( user.getType() != null) {
			// Hemos de anadir el type real
			Optional<UserType> typeOptional = userTypeRepository.findById(user.getType().getId());
			if(typeOptional.isPresent()) {
				UserType type = typeOptional.get();
				user.setType(type);
			}else {
				ValidationException.throwIt(ErrorCodes.VALIDATION, "Type operation does not exists");
			}
		}
		
		// Si es una edicion recuperamos el objeto original para que JPA pueda detectar que cambia a la hora
		// de generar la update correspondiente o si por el contrario no se requiere actualizar nada
		
		// Tambien validamos asi que el usuario a editar existe
		if(user.getId() != null) {
			Optional<User> userToUpdateOptional = userRepository.findById(user.getId());
			if(userToUpdateOptional.isPresent()) {
				User userToUpdate = userToUpdateOptional.get();
				ModelUtils.copyObjectWithOutNulls(user, userToUpdate);
				userRepository.save(userToUpdate);
			}else {
				ValidationException.throwIt(ErrorCodes.VALIDATION, "No user to edit");	
			}
		}else {
			userRepository.save(user);
		}
		
		return user;
	}
	
	/**
	 * Delete.
	 *
	 * @param id the id
	 */
	@Transactional(readOnly = false)
	public void delete(Integer id) {
		userRepository.deleteById(id);
	}


	/**
	 * Save user relations.
	 *
	 * @param tutor the tutor
	 * @param users the users
	 */
	@Transactional(readOnly = false)
	public void saveUserRelations(Integer tutor,List<Integer> users) {
		for(Integer user: users) {
			userRepository.updateRelation(tutor, user);	
		}
	}

	
	/**
	 * Gets the students by class.
	 *
	 * @param classId the class id
	 * @return the students by class
	 */
	@Transactional(readOnly = true)
	public List<User> getStudentsByClass(Integer classId){
		return userRepository.getStudentsByClass(classId);
	}
	
	/**
	 * Gets the students by course.
	 *
	 * @param courseId the course id
	 * @return the students by course
	 */
	@Transactional(readOnly = true)
	public List<User> getStudentsByCourse(Integer courseId){
		return userRepository.getStudentsByCourse(courseId);
	}
	
	/**
	 * Gets the students by course.
	 *
	 * @param user the user
	 * @return the students by course
	 */
	@Transactional(readOnly = true)
	public List<User> getRecipients(User user){
		
	    // XXX Alumnos solo listado de profesores -> en sus clases 
	    // XXX Profesores solo alumnos y Tutores legales -> de sus clases
	    // XXX Padres solo profesores -> de las clases de sus alumnos
		Integer userType = user.getType().getId();
		
		List<User> users = new ArrayList<>();
		
		if (userType == legalTutorType || userType == studentType) {
			// Todos los profesores
			Iterable<User> users1 = userRepository.findByTypeAndActive(new UserType(tutorType),true);
			Iterable<User> users2 = userRepository.findByTypeAndActive(new UserType(specialistType),true);		
			users1.forEach( (u) -> {users.add(u);});
			users2.forEach( (u) -> {users.add(u);});
			
		} else if (userType == tutorType || userType == specialistType) {
			// Todos los alumnos y tutores legales
			
			// Todos los profesores
			Iterable<User> users1 = userRepository.findByTypeAndActive(new UserType(studentType),true);
			Iterable<User> users2 = userRepository.findByTypeAndActive(new UserType(legalTutorType),true);		
			users1.forEach( (u) -> {users.add(u);});
			users2.forEach( (u) -> {users.add(u);});
		}
		
		return users;
	}
	
	/**
	 * User jobs report.
	 *
	 * @param user the user
	 * @return the list
	 */
	@Transactional(readOnly = true)
	public List<EventJob> userJobsReport(User user){
		List<EventJob> jobReports = new ArrayList<>();
		List<User> users = userRepository.findByLegalTutor(user);
		if(users !=null) {
			for (User student : users) {
				List<EventJob> jobs = eventJobRepository.findByStudent(student);
				jobReports.addAll(jobs);
			}
		}
		return jobReports;
	}
}