Merge branch '42--2' into 'dev'

Resolve "Форма добавления и фильтра участников гранта"

Closes #42

See merge request romanov73/ng-tracker!44
environments/staging/deployments/29
Anton Romanov 5 years ago
commit 5a55e3cfc8

@ -1,46 +1,46 @@
## Краткое описание задачи ## Краткое описание задачи
Что требуется сделать Что требуется сделать
## `Опционально` Список верстаемых страниц ## `Опционально` Список верстаемых страниц
Будут затронуты страницы: Будут затронуты страницы:
* page1.html * page1.html
* page2.html * page2.html
* page3.html * page3.html
## `Опционально` Список затрагиваемых модулей ## `Опционально` Список затрагиваемых модулей
При реализации задачи потребуется также реализовать методы контроллера При реализации задачи потребуется также реализовать методы контроллера
## `Опционально` Список реализуемых функций ## `Опционально` Список реализуемых функций
После выполнения задачи станет доступным: После выполнения задачи станет доступным:
* просмотр `entity_name` * просмотр `entity_name`
* редактирование `entity_name` * редактирование `entity_name`
* валидация `entity_name` * валидация `entity_name`
## `Опционально` Сценарии работы ## `Опционально` Сценарии работы
Сценарий просмотра: Сценарий просмотра:
1. Зайти на главную страницу приложения 1. Зайти на главную страницу приложения
2. Перейти в раздел `section_name` 2. Перейти в раздел `section_name`
3. Перейти к списку `entity_name` 3. Перейти к списку `entity_name`
4. Выбрать нужную `entity_name` и нажать на нее 4. Выбрать нужную `entity_name` и нажать на нее
Сценарий редактирования: Сценарий редактирования:
1. Зайти на главную страницу приложения 1. Зайти на главную страницу приложения
2. Перейти в раздел `section_name` 2. Перейти в раздел `section_name`
3. Перейти к списку `entity_name` 3. Перейти к списку `entity_name`
4. Выбрать нужную `entity_name` и нажать на нее 4. Выбрать нужную `entity_name` и нажать на нее
5. Внести нужные правки в поля и сохранить 5. Внести нужные правки в поля и сохранить
## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы ## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы
* Сверстаны страницы page1.hml, page2.hml, page3.hml * Сверстаны страницы page1.hml, page2.hml, page3.hml
* Реализован контроллер для обслуживания страниц * Реализован контроллер для обслуживания страниц
* Сохранение в БД еще не реализовано * Сохранение в БД еще не реализовано
* Валидация происходит по полям `field1, field2` * Валидация происходит по полям `field1, field2`
* Сценарий просмотра проверяется при ручном внечении записей в БД * Сценарий просмотра проверяется при ручном внечении записей в БД

@ -13,6 +13,7 @@ import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.grant.model.Grant; import ru.ulstu.grant.model.Grant;
import ru.ulstu.grant.model.GrantDto; import ru.ulstu.grant.model.GrantDto;
import ru.ulstu.grant.service.GrantService; import ru.ulstu.grant.service.GrantService;
import ru.ulstu.user.model.User;
import springfox.documentation.annotations.ApiIgnore; import springfox.documentation.annotations.ApiIgnore;
import javax.validation.Valid; import javax.validation.Valid;
@ -24,7 +25,6 @@ import static org.springframework.util.StringUtils.isEmpty;
import static ru.ulstu.core.controller.Navigation.GRANTS_PAGE; import static ru.ulstu.core.controller.Navigation.GRANTS_PAGE;
import static ru.ulstu.core.controller.Navigation.GRANT_PAGE; import static ru.ulstu.core.controller.Navigation.GRANT_PAGE;
import static ru.ulstu.core.controller.Navigation.REDIRECT_TO; import static ru.ulstu.core.controller.Navigation.REDIRECT_TO;
import static ru.ulstu.core.controller.Navigation.hasErrors;
@Controller() @Controller()
@ -57,27 +57,42 @@ public class GrantController {
} }
@PostMapping(value = "/grant", params = "save") @PostMapping(value = "/grant", params = "save")
public String save(@Valid GrantDto grantDto, Errors errors) throws IOException { public String save(@Valid GrantDto grantDto, Errors errors)
throws IOException {
filterEmptyDeadlines(grantDto); filterEmptyDeadlines(grantDto);
if (grantDto.getDeadlines().isEmpty()) { if (grantDto.getDeadlines().isEmpty()) {
errors.rejectValue("deadlines", "errorCode", "Не может быть пустым"); errors.rejectValue("deadlines", "errorCode", "Не может быть пусто");
}
if (grantDto.getLeaderId().equals(-1)) {
errors.rejectValue("leaderId", "errorCode", "Укажите руководителя");
}
if (errors.hasErrors()) {
return GRANT_PAGE;
} }
hasErrors(errors, GRANT_PAGE);
grantService.save(grantDto); grantService.save(grantDto);
return String.format(REDIRECT_TO, GRANTS_PAGE); return String.format(REDIRECT_TO, GRANTS_PAGE);
} }
@PostMapping(value = "/grant", params = "filterUsers")
public String filterUsers() {
return GRANT_PAGE;
}
@PostMapping(value = "/grant", params = "addDeadline") @PostMapping(value = "/grant", params = "addDeadline")
public String addDeadline(@Valid GrantDto grantDto, Errors errors) { public String addDeadline(@Valid GrantDto grantDto, Errors errors) {
filterEmptyDeadlines(grantDto); filterEmptyDeadlines(grantDto);
hasErrors(errors, GRANT_PAGE); if (errors.hasErrors()) {
return GRANT_PAGE;
}
grantDto.getDeadlines().add(new Deadline()); grantDto.getDeadlines().add(new Deadline());
return GRANT_PAGE; return GRANT_PAGE;
} }
@PostMapping(value = "/grant", params = "createProject") @PostMapping(value = "/grant", params = "createProject")
public String createProject(@Valid GrantDto grantDto, Errors errors) { public String createProject(@Valid GrantDto grantDto, Errors errors) {
hasErrors(errors, GRANT_PAGE); if (errors.hasErrors()) {
return GRANT_PAGE;
}
grantService.createProject(grantDto); grantService.createProject(grantDto);
return GRANT_PAGE; return GRANT_PAGE;
} }
@ -93,6 +108,11 @@ public class GrantController {
return grantService.getGrantStatuses(); return grantService.getGrantStatuses();
} }
@ModelAttribute("allAuthors")
public List<User> getAllAuthors(GrantDto grantDto) {
return grantService.getGrantAuthors(grantDto);
}
private void filterEmptyDeadlines(GrantDto grantDto) { private void filterEmptyDeadlines(GrantDto grantDto) {
grantDto.setDeadlines(grantDto.getDeadlines().stream() grantDto.setDeadlines(grantDto.getDeadlines().stream()
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))

@ -2,28 +2,35 @@ package ru.ulstu.grant.model;
import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotBlank;
import ru.ulstu.core.model.BaseEntity; import ru.ulstu.core.model.BaseEntity;
import ru.ulstu.core.model.UserContainer;
import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.file.model.FileData; import ru.ulstu.file.model.FileData;
import ru.ulstu.project.model.Project; import ru.ulstu.project.model.Project;
import ru.ulstu.user.model.User;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType; import javax.persistence.EnumType;
import javax.persistence.Enumerated; import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table; import javax.persistence.Table;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
@Entity @Entity
@Table(name = "grants") @Table(name = "grants")
public class Grant extends BaseEntity { public class Grant extends BaseEntity implements UserContainer {
public enum GrantStatus { public enum GrantStatus {
APPLICATION("Заявка"), APPLICATION("Заявка"),
ON_COMPETITION("Отправлен на конкурс"), ON_COMPETITION("Отправлен на конкурс"),
@ -52,6 +59,7 @@ public class Grant extends BaseEntity {
@OneToMany(cascade = CascadeType.ALL) @OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "grant_id") @JoinColumn(name = "grant_id")
@OrderBy("date")
private List<Deadline> deadlines = new ArrayList<>(); private List<Deadline> deadlines = new ArrayList<>();
//Описание гранта //Описание гранта
@ -67,6 +75,14 @@ public class Grant extends BaseEntity {
@JoinColumn(name = "project_id") @JoinColumn(name = "project_id")
private Project project; private Project project;
@ManyToMany(fetch = FetchType.EAGER)
private Set<User> authors = new HashSet<>();
@NotNull
@ManyToOne
@JoinColumn(name = "leader_id")
private User leader;
public GrantStatus getStatus() { public GrantStatus getStatus() {
return status; return status;
} }
@ -115,6 +131,27 @@ public class Grant extends BaseEntity {
this.project = project; this.project = project;
} }
public Set<User> getAuthors() {
return authors;
}
public void setAuthors(Set<User> authors) {
this.authors = authors;
}
@Override
public Set<User> getUsers() {
return getAuthors();
}
public User getLeader() {
return leader;
}
public void setLeader(User leader) {
this.leader = leader;
}
public Optional<Deadline> getNextDeadline() { public Optional<Deadline> getNextDeadline() {
return deadlines return deadlines
.stream() .stream()

@ -2,14 +2,22 @@ package ru.ulstu.grant.model;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.NotEmpty; import org.hibernate.validator.constraints.NotEmpty;
import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.model.Deadline;
import ru.ulstu.project.model.ProjectDto; import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.user.model.UserDto;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static ru.ulstu.core.util.StreamApiUtils.convert;
public class GrantDto { public class GrantDto {
private final static int MAX_AUTHORS_LENGTH = 60;
private Integer id; private Integer id;
@NotEmpty @NotEmpty
private String title; private String title;
@ -18,6 +26,12 @@ public class GrantDto {
private String comment; private String comment;
private String applicationFileName; private String applicationFileName;
private ProjectDto project; private ProjectDto project;
private Set<Integer> authorIds;
private Set<UserDto> authors;
private Integer leaderId;
private boolean wasLeader;
private boolean hasAge;
private boolean hasDegree;
public GrantDto() { public GrantDto() {
deadlines.add(new Deadline()); deadlines.add(new Deadline());
@ -29,7 +43,13 @@ public class GrantDto {
@JsonProperty("status") Grant.GrantStatus status, @JsonProperty("status") Grant.GrantStatus status,
@JsonProperty("deadlines") List<Deadline> deadlines, @JsonProperty("deadlines") List<Deadline> deadlines,
@JsonProperty("comment") String comment, @JsonProperty("comment") String comment,
@JsonProperty("project") ProjectDto project) { @JsonProperty("project") ProjectDto project,
@JsonProperty("authorIds") Set<Integer> authorIds,
@JsonProperty("authors") Set<UserDto> authors,
@JsonProperty("leader") Integer leaderId,
@JsonProperty("wasLeader") boolean wasLeader,
@JsonProperty("hasAge") boolean hasAge,
@JsonProperty("hasDegree") boolean hasDegree) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.status = status; this.status = status;
@ -37,6 +57,11 @@ public class GrantDto {
this.comment = comment; this.comment = comment;
this.applicationFileName = null; this.applicationFileName = null;
this.project = project; this.project = project;
this.authors = authors;
this.leaderId = leaderId;
this.wasLeader = wasLeader;
this.hasAge = hasAge;
this.hasDegree = hasDegree;
} }
public GrantDto(Grant grant) { public GrantDto(Grant grant) {
@ -47,6 +72,12 @@ public class GrantDto {
this.comment = grant.getComment(); this.comment = grant.getComment();
this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject()); this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject());
this.applicationFileName = grant.getApplication() == null ? null : grant.getApplication().getName(); this.applicationFileName = grant.getApplication() == null ? null : grant.getApplication().getName();
this.authorIds = convert(grant.getAuthors(), user -> user.getId());
this.authors = convert(grant.getAuthors(), UserDto::new);
this.leaderId = grant.getLeader().getId();
this.wasLeader = false;
this.hasAge = false;
this.hasDegree = false;
} }
public Integer getId() { public Integer getId() {
@ -104,4 +135,59 @@ public class GrantDto {
public void setApplicationFileName(String applicationFileName) { public void setApplicationFileName(String applicationFileName) {
this.applicationFileName = applicationFileName; this.applicationFileName = applicationFileName;
} }
public Set<Integer> getAuthorIds() {
return authorIds;
}
public void setAuthorIds(Set<Integer> authorIds) {
this.authorIds = authorIds;
}
public Set<UserDto> getAuthors() {
return authors;
}
public void setAuthors(Set<UserDto> authors) {
this.authors = authors;
}
public String getAuthorsString() {
return StringUtils.abbreviate(authors
.stream()
.map(author -> author.getLastName())
.collect(Collectors.joining(", ")), MAX_AUTHORS_LENGTH);
}
public Integer getLeaderId() {
return leaderId;
}
public void setLeaderId(Integer leaderId) {
this.leaderId = leaderId;
}
public boolean isWasLeader() {
return wasLeader;
}
public void setWasLeader(boolean wasLeader) {
this.wasLeader = wasLeader;
}
public boolean isHasAge() {
return hasAge;
}
public void setHasAge(boolean hasAge) {
this.hasAge = hasAge;
}
public boolean isHasDegree() {
return hasDegree;
}
public void setHasDegree(boolean hasDegree) {
this.hasDegree = hasDegree;
}
} }

@ -1,19 +0,0 @@
package ru.ulstu.grant.model;
public class GrantStatusDto {
private final String id;
private final String name;
public GrantStatusDto(Grant.GrantStatus status) {
this.id = status.name();
this.name = status.getStatusName();
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}

@ -3,6 +3,9 @@ package ru.ulstu.grant.repository;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.grant.model.Grant; import ru.ulstu.grant.model.Grant;
import java.util.List;
public interface GrantRepository extends JpaRepository<Grant, Integer> { public interface GrantRepository extends JpaRepository<Grant, Integer> {
List<Grant> findByStatus(Grant.GrantStatus status);
} }

@ -12,11 +12,14 @@ import ru.ulstu.grant.repository.GrantRepository;
import ru.ulstu.project.model.Project; import ru.ulstu.project.model.Project;
import ru.ulstu.project.model.ProjectDto; import ru.ulstu.project.model.ProjectDto;
import ru.ulstu.project.service.ProjectService; import ru.ulstu.project.service.ProjectService;
import ru.ulstu.user.model.User;
import ru.ulstu.user.service.UserService;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.util.ObjectUtils.isEmpty; import static org.springframework.util.ObjectUtils.isEmpty;
import static ru.ulstu.core.util.StreamApiUtils.convert; import static ru.ulstu.core.util.StreamApiUtils.convert;
@ -30,15 +33,18 @@ public class GrantService {
private final ProjectService projectService; private final ProjectService projectService;
private final DeadlineService deadlineService; private final DeadlineService deadlineService;
private final FileService fileService; private final FileService fileService;
private final UserService userService;
public GrantService(GrantRepository grantRepository, public GrantService(GrantRepository grantRepository,
FileService fileService, FileService fileService,
DeadlineService deadlineService, DeadlineService deadlineService,
ProjectService projectService) { ProjectService projectService,
UserService userService) {
this.grantRepository = grantRepository; this.grantRepository = grantRepository;
this.projectService = projectService;
this.fileService = fileService; this.fileService = fileService;
this.deadlineService = deadlineService; this.deadlineService = deadlineService;
this.projectService = projectService;
this.userService = userService;
} }
public List<Grant> findAll() { public List<Grant> findAll() {
@ -73,6 +79,13 @@ public class GrantService {
if (grantDto.getApplicationFileName() != null) { if (grantDto.getApplicationFileName() != null) {
grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName())); grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName()));
} }
grant.getAuthors().clear();
if (grantDto.getAuthorIds() != null && !grantDto.getAuthorIds().isEmpty()) {
grantDto.getAuthorIds().forEach(authorIds -> grant.getAuthors().add(userService.findById(authorIds)));
}
if (grantDto.getLeaderId() != null) {
grant.setLeader(userService.findById(grantDto.getLeaderId()));
}
return grant; return grant;
} }
@ -84,7 +97,6 @@ public class GrantService {
@Transactional @Transactional
public Integer update(GrantDto grantDto) throws IOException { public Integer update(GrantDto grantDto) throws IOException {
Grant grant = grantRepository.findOne(grantDto.getId()); Grant grant = grantRepository.findOne(grantDto.getId());
Grant.GrantStatus oldStatus = grant.getStatus();
if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) { if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) {
fileService.deleteFile(grant.getApplication()); fileService.deleteFile(grant.getApplication());
} }
@ -98,7 +110,6 @@ public class GrantService {
if (grant.getApplication() != null) { if (grant.getApplication() != null) {
fileService.deleteFile(grant.getApplication()); fileService.deleteFile(grant.getApplication());
} }
//возможно при удалении гранта будет удаляться и проект, к нему привязанный
grantRepository.delete(grant); grantRepository.delete(grant);
} }
@ -107,13 +118,15 @@ public class GrantService {
} }
@Transactional @Transactional
public Grant create(String title, Project projectId, Date deadlineDate) { public Grant create(String title, Project projectId, Date deadlineDate, User user) {
Grant grant = new Grant(); Grant grant = new Grant();
grant.setTitle(title); grant.setTitle(title);
grant.setComment("Комментарий к гранту 1"); grant.setComment("Комментарий к гранту 1");
grant.setProject(projectId); grant.setProject(projectId);
grant.setStatus(APPLICATION); grant.setStatus(APPLICATION);
grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн")); grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн"));
grant.getAuthors().add(user);
grant.setLeader(user);
grant = grantRepository.save(grant); grant = grantRepository.save(grant);
return grant; return grant;
} }
@ -125,4 +138,22 @@ public class GrantService {
update(grantDto); update(grantDto);
} }
} }
public List<User> getGrantAuthors(GrantDto grantDto) {
List<User> filteredUsers = userService.filterByAgeAndDegree(grantDto.isHasAge(), grantDto.isHasDegree());
if (grantDto.isWasLeader()) {
filteredUsers = filteredUsers
.stream()
.filter(getCompletedGrantLeaders()::contains)
.collect(Collectors.toList());
}
return filteredUsers;
}
private List<User> getCompletedGrantLeaders() {
return grantRepository.findByStatus(Grant.GrantStatus.COMPLETED)
.stream()
.map(Grant::getLeader)
.collect(Collectors.toList());
}
} }

@ -7,6 +7,8 @@ import ru.ulstu.core.model.BaseEntity;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.ManyToMany; import javax.persistence.ManyToMany;
@ -85,6 +87,28 @@ public class User extends BaseEntity {
@BatchSize(size = 20) @BatchSize(size = 20)
private Set<UserRole> roles; private Set<UserRole> roles;
@Column(name = "birth_date")
@Temporal(TemporalType.TIMESTAMP)
private Date birthDate;
public enum UserDegree {
CANDIDATE("Кандидат технических наук"),
DOCTOR("Доктор технических наук");
private String degreeName;
UserDegree(String degreeName) {
this.degreeName = degreeName;
}
public String getDegreeName() {
return degreeName;
}
}
@Enumerated(value = EnumType.STRING)
private UserDegree degree;
public User() { public User() {
roles = new HashSet<>(); roles = new HashSet<>();
activated = false; activated = false;
@ -189,6 +213,22 @@ public class User extends BaseEntity {
this.patronymic = patronymic; this.patronymic = patronymic;
} }
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public UserDegree getDegree() {
return degree;
}
public void setDegree(UserDegree degree) {
this.degree = degree;
}
public String getUserAbbreviate() { public String getUserAbbreviate() {
return String.format(USER_ABBREVIATE_TEMPLATE, return String.format(USER_ABBREVIATE_TEMPLATE,
lastName == null ? "" : lastName, lastName == null ? "" : lastName,

@ -15,6 +15,7 @@ import ru.ulstu.user.controller.UserController;
import javax.validation.constraints.Pattern; import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -70,6 +71,10 @@ public class UserDto implements OdinDto {
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50) @Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
private String passwordConfirm; private String passwordConfirm;
private Date birthDate;
private User.UserDegree degree;
public UserDto() { public UserDto() {
activated = false; activated = false;
roles = new LinkedHashSet<>(); roles = new LinkedHashSet<>();
@ -86,6 +91,8 @@ public class UserDto implements OdinDto {
this.roles.addAll(user.getRoles().stream() this.roles.addAll(user.getRoles().stream()
.map(UserRoleDto::new) .map(UserRoleDto::new)
.collect(Collectors.toList())); .collect(Collectors.toList()));
this.birthDate = user.getBirthDate();
this.degree = user.getDegree();
} }
public Integer getId() { public Integer getId() {
@ -163,6 +170,22 @@ public class UserDto implements OdinDto {
return passwordConfirm; return passwordConfirm;
} }
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
public User.UserDegree getDegree() {
return degree;
}
public void setDegree(User.UserDegree degree) {
this.degree = degree;
}
@JsonIgnore @JsonIgnore
public boolean isPasswordsValid() { public boolean isPasswordsValid() {
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) { if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
@ -188,6 +211,8 @@ public class UserDto implements OdinDto {
", roles=" + roles + ", roles=" + roles +
", password='" + password + '\'' + ", password='" + password + '\'' +
", passwordConfirm='" + passwordConfirm + '\'' + ", passwordConfirm='" + passwordConfirm + '\'' +
", birthDate='" + birthDate + '\'' +
", degree='" + degree + '\'' +
'}'; '}';
} }
} }

@ -2,6 +2,8 @@ package ru.ulstu.user.repository;
import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.ulstu.user.model.User; import ru.ulstu.user.model.User;
import java.util.Date; import java.util.Date;
@ -25,4 +27,11 @@ public interface UserRepository extends JpaRepository<User, Integer> {
@EntityGraph(attributePaths = "roles") @EntityGraph(attributePaths = "roles")
User findOneWithRolesByLogin(String login); User findOneWithRolesByLogin(String login);
@Query("SELECT u FROM User u " +
"WHERE (YEAR(CURRENT_DATE) - YEAR(u.birthDate) < 35 OR :hasAge = FALSE) " +
"AND (u.degree = 'CANDIDATE' OR :hasDegree = FALSE)" +
"ORDER BY u.lastName")
List<User> filterByAgeAndDegree(@Param("hasAge") boolean hasAge,
@Param("hasDegree") boolean hasDegree);
} }

@ -324,4 +324,8 @@ public class UserService implements UserDetailsService {
} }
return user; return user;
} }
public List<User> filterByAgeAndDegree(boolean hasDegree, boolean hasAge) {
return userRepository.filterByAgeAndDegree(hasDegree, hasAge);
}
} }

@ -0,0 +1,26 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="tanya" id="20190402_000000-1">
<addColumn tableName="grants">
<column name="leader_id" type="integer"></column>
</addColumn>
<addForeignKeyConstraint baseTableName="grants" baseColumnNames="leader_id"
constraintName="fk_grants_leader_id" referencedTableName="users"
referencedColumnNames="id"/>
</changeSet>
<changeSet author="tanya" id="20190402_000000-2">
<createTable tableName="grants_authors">
<column name="grant_id" type="integer"/>
<column name="authors_id" type="integer"/>
</createTable>
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="grant_id"
constraintName="fk_grants_grants_authors" referencedTableName="grants"
referencedColumnNames="id"/>
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="authors_id"
constraintName="fk_user_grants_authors" referencedTableName="users"
referencedColumnNames="id"/>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,13 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="tanya" id="20190404_000000-1">
<addColumn tableName="users">
<column name="birth_date" type="timestamp"></column>
</addColumn>
<addColumn tableName="users">
<column name="degree" type="varchar(255)"></column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -27,4 +27,6 @@
<include file="db/changelog-20190418_000000-schema.xml"/> <include file="db/changelog-20190418_000000-schema.xml"/>
<include file="db/changelog-20190323_000001-schema.xml"/> <include file="db/changelog-20190323_000001-schema.xml"/>
<include file="db/common/changelog-20190312_130000-schema.xml"/> <include file="db/common/changelog-20190312_130000-schema.xml"/>
<include file="db/changelog-20190402_000000-schema.xml"/>
<include file="db/changelog-20190404_000000-schema.xml"/>
</databaseChangeLog> </databaseChangeLog>

@ -0,0 +1,12 @@
.div-deadline-date {
padding-right: 0px;
}
.form-deadline-date{
padding-right: 3px;
}
.div-deadline-description{
padding-left: 5px;
padding-right: 5px;
}

@ -9,7 +9,7 @@
<span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/> <span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/>
<a th:href="@{'grant?id='+${grant.id}}"> <a th:href="@{'grant?id='+${grant.id}}">
<span class="h6" th:text="${grant.title}"/> <span class="h6" th:text="${grant.title}"/>
<span class="text-muted" th:text="${grant.comment}"/> <span class="text-muted" th:text="${grant.authorsString}"/>
</a> </a>
<input class="id-class" type="hidden" th:value="${grant.id}"/> <input class="id-class" type="hidden" th:value="${grant.id}"/>
<a class="remove-paper pull-right d-none" th:href="@{'/grants/delete/'+${grant.id}}" <a class="remove-paper pull-right d-none" th:href="@{'/grants/delete/'+${grant.id}}"

@ -4,7 +4,7 @@
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
</head> </head>
<body> <body>
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x"> <span th:fragment="grantStatus (grantStatus)" class="fa-stack fa-1x">
<th:block th:switch="${grantStatus.name()}"> <th:block th:switch="${grantStatus.name()}">
<div th:case="'APPLICATION'"> <div th:case="'APPLICATION'">
<i class="fa fa-circle fa-stack-2x text-draft"></i> <i class="fa fa-circle fa-stack-2x text-draft"></i>

@ -3,13 +3,12 @@
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html"> layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head> <head>
<link rel="stylesheet" href="../css/grant.css"/>
</head> </head>
<body> <body>
<div class="container" layout:fragment="content"> <div class="container" layout:fragment="content">
<section id="grant">
<section id="paper">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-lg-12 text-center"> <div class="col-lg-12 text-center">
@ -17,6 +16,7 @@
<div th:replace="grants/fragments/grantNavigationFragment"/> <div th:replace="grants/fragments/grantNavigationFragment"/>
</div> </div>
</div> </div>
<hr/>
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<form id="grant-form" method="post" th:action="@{'/grants/grant?id='+ *{id == null ? '' : id} + ''}" <form id="grant-form" method="post" th:action="@{'/grants/grant?id='+ *{id == null ? '' : id} + ''}"
@ -33,7 +33,6 @@
class="alert alert-danger">Incorrect title</p> class="alert alert-danger">Incorrect title</p>
<p class="help-block text-danger"></p> <p class="help-block text-danger"></p>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="status">Статус:</label> <label for="status">Статус:</label>
<select class="form-control" th:field="*{status}" id="status"> <select class="form-control" th:field="*{status}" id="status">
@ -51,11 +50,11 @@
<label>Дедлайны показателей:</label> <label>Дедлайны показателей:</label>
<div class="row" th:each="deadline, rowStat : *{deadlines}"> <div class="row" th:each="deadline, rowStat : *{deadlines}">
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/> <input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
<div class="col-6"> <div class="col-6 div-deadline-date">
<input type="date" class="form-control" name="deadline" <input type="date" class="form-control form-deadline-date" name="deadline"
th:field="*{deadlines[__${rowStat.index}__].date}"/> th:field="*{deadlines[__${rowStat.index}__].date}"/>
</div> </div>
<div class="col-4"> <div class="col-4 div-deadline-description">
<input class="form-control" type="text" placeholder="Описание" <input class="form-control" type="text" placeholder="Описание"
th:field="*{deadlines[__${rowStat.index}__].description}"/> th:field="*{deadlines[__${rowStat.index}__].description}"/>
</div> </div>
@ -72,31 +71,101 @@
class="alert alert-danger">Incorrect title</p> class="alert alert-danger">Incorrect title</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary" value="Добавить <input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
дедлайн"/> value="Добавить дедлайн"/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="loader">Загрузить заявку:</label> <label for="loader">Загрузить заявку:</label>
<div id="loader"> <div id="loader">
</div> </div>
</div> </div>
</div>
<div class="col-md-6 col-sm-12">
<label data-toggle="collapse"
href="#collapse-filter"
aria-expanded="false"
aria-controls="collapse-filter">Фильтр рабочей группы
</label>
<div th:class="${grantDto.wasLeader || grantDto.hasAge || grantDto.hasDegree} ?
'form-check' : 'form-check collapse'" id="collapse-filter">
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f1"
th:field="*{wasLeader}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f1">Был руководителем проекта</label>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f2"
th:field="*{hasAge}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f2">Младше 35 лет</label>
</div>
</div>
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f3"
th:field="*{hasDegree}" th:onclick="|$('#filter').click();|"/>
<label class="form-check-label" for="f3">епень к.т.н.</label>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f4"/>
<label class="form-check-label" for="f4">Более 3-х публикаций в
scopus</label>
</div>
</div>
<div class="row">
<div class="col">
<input class="form-check-input" type="checkbox" id="f5"/>
<label class="form-check-label" for="f5">Наличие ВАК статей</label> <br/>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id="f6"/>
<label class="form-check-label" for="f6">Наименьшая загруженность</label>
</div>
</div>
<input type="submit" hidden="hidden" id="filter" name="filterUsers"
value="Применить фильтр"/>
<hr/>
</div>
<div class="form-group">
<label>Руководитель проекта:</label>
<select class="form-control" th:field="*{leaderId}" id="leaderId"
onchange="updateAuthors();">
<option selected="selected" hidden="hidden" th:value="-1">-- Выберите
руководителя --
</option>
<option th:each="leader : ${allAuthors}" th:value="${leader.id}"
th:text="${leader.lastName}"> Руководитель
</option>
</select>
<p th:if="${#fields.hasErrors('leaderId')}" th:errors="*{leaderId}"
class="alert alert-danger">Choose leader</p>
<p class="help-block text-danger"></p>
</div>
<div class="form-group">
<label>Участники гранта:</label>
<select class="selectpicker form-control" multiple="true"
title="-- Выберите участников --" id="authors"
th:field="*{authorIds}">
<option th:each="author : ${allAuthors}" th:value="${author.id}"
th:text="${author.lastName}"> Участник
</option>
</select>
</div>
<div class="form-group"> <div class="form-group">
<label>Список статей:</label> <label>Список статей:</label>
<p><a href="./#" class="btn btn-primary" ><i class="fa fa-plus-circle" aria-hidden="true"> <p><a href="./#" class="btn btn-primary"><i class="fa fa-plus-circle"
aria-hidden="true">
</i> Добавить статью</a></p> </i> Добавить статью</a></p>
</div> </div>
<div class="form-group"> <div class="form-group">
<div th:if="*{project} == null"> <div th:if="*{project} == null">
<input type="submit" name="createProject" class="btn btn-primary" <input type="submit" name="createProject" class="btn btn-primary"
value="Добавить проект"/> value="Добавить проект"/>
</div> </div>
<input type = "hidden" th:field="*{project.id}"/> <input type = "hidden" th:field="*{project.id}"/>
</div> </div>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
<div class="col-lg-12"> <div class="col-lg-12">
<div class="form-group"> <div class="form-group">
@ -132,6 +201,16 @@
$('.selectpicker').selectpicker(); $('.selectpicker').selectpicker();
}); });
/*]]>*/ /*]]>*/
</script>
<script type="text/javascript">
function updateAuthors() {
$("#authors").val('default');
$("#authors").selectpicker("refresh");
$("#authors").children('option:disabled').prop('disabled', false);
var lid = $("#leaderId option:selected").val();
$("#authors [value='" + lid + "']").attr("disabled", "disabled");
}
</script> </script>
</div> </div>
</body> </body>

@ -82,7 +82,7 @@
</div> </div>
</div> </div>
<div class="col-md-4 col-sm-6 portfolio-item"> <div class="col-md-4 col-sm-6 portfolio-item">
<a class="portfolio-link" href="./students/tasks"> <a class="portfolio-link" href="./students/tasks">
<div class="portfolio-hover"> <div class="portfolio-hover">
<div class="portfolio-hover-content"> <div class="portfolio-hover-content">
<i class="fa fa-arrow-right fa-3x"></i> <i class="fa fa-arrow-right fa-3x"></i>

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html"> layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
<head> <head>
</head> </head>
@ -19,7 +19,8 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<form id="project-form" method="post" th:action="@{'/projects/project?id='+ *{id == null ? '' : id} + ''}" <form id="project-form" method="post"
th:action="@{'/projects/project?id='+ *{id == null ? '' : id} + ''}"
th:object="${projectDto}"> th:object="${projectDto}">
<div class="row"> <div class="row">
<div class="col-md-6 col-sm-12"> <div class="col-md-6 col-sm-12">
@ -55,7 +56,7 @@
<input type="submit" id="createGrant" name="createGrant" class="btn btn-primary" <input type="submit" id="createGrant" name="createGrant" class="btn btn-primary"
value="Добавить грант"/> value="Добавить грант"/>
</div> </div>
<input type = "hidden" th:field="*{grant.id}"/> <input type="hidden" th:field="*{grant.id}"/>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -88,7 +89,8 @@
class="alert alert-danger">Incorrect title</p> class="alert alert-danger">Incorrect title</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary" value="Добавить <input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
value="Добавить
дедлайн"/> дедлайн"/>
</div> </div>
@ -107,7 +109,8 @@
type="submit"> type="submit">
Сохранить Сохранить
</button> </button>
<button id="cancelButton" class="btn btn-default text-uppercase" href="/projects/projects"> <button id="cancelButton" class="btn btn-default text-uppercase"
href="/projects/projects">
Отмена Отмена
</button> </button>
</div> </div>
@ -135,6 +138,7 @@
$('.selectpicker').selectpicker(); $('.selectpicker').selectpicker();
}); });
/*]]>*/ /*]]>*/
</script> </script>
</div> </div>
</body> </body>

@ -16,7 +16,7 @@
<div class="row justify-content-center" id="dashboard"> <div class="row justify-content-center" id="dashboard">
<div th:replace="students/fragments/taskDashboardFragment"/> <div th:replace="students/fragments/taskDashboardFragment"/>
<!--<th:block th:each="task : ${tasks}">--> <!--<th:block th:each="task : ${tasks}">-->
<!--<div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>--> <!--<div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>-->
<!--</th:block>--> <!--</th:block>-->
</div> </div>
</div> </div>

Loading…
Cancel
Save