diff --git a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java index 4c5ea91..5aecb51 100644 --- a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java +++ b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java @@ -13,7 +13,6 @@ import ru.ulstu.conference.model.ConferenceDto; import ru.ulstu.conference.model.ConferenceFilterDto; import ru.ulstu.conference.model.ConferenceUser; import ru.ulstu.conference.service.ConferenceService; -import ru.ulstu.deadline.model.Deadline; import ru.ulstu.user.model.User; import springfox.documentation.annotations.ApiIgnore; @@ -22,9 +21,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import java.util.stream.Collectors; -import static org.springframework.util.StringUtils.isEmpty; import static ru.ulstu.core.controller.Navigation.CONFERENCES_PAGE; import static ru.ulstu.core.controller.Navigation.CONFERENCE_PAGE; import static ru.ulstu.core.controller.Navigation.REDIRECT_TO; @@ -77,7 +74,8 @@ public class ConferenceController { @PostMapping(value = "/conference", params = "save") public String save(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException { - filterEmptyDeadlines(conferenceDto); + conferenceService.filterEmptyDeadlines(conferenceDto); + conferenceService.checkEmptyFieldsOfDeadline(conferenceDto, errors); if (errors.hasErrors()) { return CONFERENCE_PAGE; } @@ -87,11 +85,11 @@ public class ConferenceController { @PostMapping(value = "/conference", params = "addDeadline") public String addDeadline(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException { - filterEmptyDeadlines(conferenceDto); + conferenceService.filterEmptyDeadlines(conferenceDto); if (errors.hasErrors()) { return CONFERENCE_PAGE; } - conferenceDto.getDeadlines().add(new Deadline()); + conferenceService.addDeadline(conferenceDto); return CONFERENCE_PAGE; } @@ -167,9 +165,4 @@ public class ConferenceController { return years; } - private void filterEmptyDeadlines(ConferenceDto conferenceDto) { - conferenceDto.setDeadlines(conferenceDto.getDeadlines().stream() - .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) - .collect(Collectors.toList())); - } } diff --git a/src/main/java/ru/ulstu/conference/model/Conference.java b/src/main/java/ru/ulstu/conference/model/Conference.java index 3f77699..7e18ac0 100644 --- a/src/main/java/ru/ulstu/conference/model/Conference.java +++ b/src/main/java/ru/ulstu/conference/model/Conference.java @@ -21,8 +21,10 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.Optional; @Entity @Table(name = "conference") @@ -57,6 +59,7 @@ public class Conference extends BaseEntity { @JoinTable(name = "paper_conference", joinColumns = {@JoinColumn(name = "conference_id")}, inverseJoinColumns = {@JoinColumn(name = "paper_id")}) + @Fetch(FetchMode.SUBSELECT) private List papers = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @@ -135,4 +138,13 @@ public class Conference extends BaseEntity { public void setUsers(List users) { this.users = users; } + + public Optional getNextDeadline() { + return deadlines + .stream() + .filter(deadline -> deadline.getDate() != null) + .sorted(Comparator.comparing(Deadline::getDate)) + .filter(d -> d.getDate().after(new Date())) + .findFirst(); + } } diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceNotificationService.java b/src/main/java/ru/ulstu/conference/service/ConferenceNotificationService.java index ff9f69a..ddb8130 100644 --- a/src/main/java/ru/ulstu/conference/service/ConferenceNotificationService.java +++ b/src/main/java/ru/ulstu/conference/service/ConferenceNotificationService.java @@ -1,7 +1,113 @@ package ru.ulstu.conference.service; +import com.google.common.collect.ImmutableMap; import org.springframework.stereotype.Service; +import ru.ulstu.conference.model.Conference; +import ru.ulstu.core.util.DateUtils; +import ru.ulstu.ping.service.PingService; +import ru.ulstu.user.service.MailService; +import ru.ulstu.user.service.UserService; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Map; @Service public class ConferenceNotificationService { + + private final static int YESTERDAY = -1; + private final static int DAYS_TO_DEADLINE_NOTIFICATION = 7; + private final static String TEMPLATE_PING = "conferencePingNotification"; + private final static String TEMPLATE_DEADLINE = "conferenceDeadlineNotification"; + private final static String TEMPLATE_CREATE = "conferenceCreateNotification"; + private final static String TEMPLATE_UPDATE_DEADLINES = "conferenceUpdateDeadlinesNotification"; + private final static String TEMPLATE_UPDATE_DATES = "conferenceUpdateDatesNotification"; + + private final static String TITLE_PING = "Обратите внимание на конференцию: %s"; + private final static String TITLE_DEADLINE = "Приближается дедлайн конференции: %s"; + private final static String TITLE_CREATE = "Создана новая конференция: %s"; + private final static String TITLE_UPDATE_DEADLINES = "Изменения дедлайнов в конференции: %s"; + private final static String TITLE_UPDATE_DATES = "Изменение дат проведения конференции: %s"; + + private final MailService mailService; + private final UserService userService; + private final PingService pingService; + + public ConferenceNotificationService(MailService mailService, + UserService userService, + PingService pingService) { + this.mailService = mailService; + this.userService = userService; + this.pingService = pingService; + } + + public void sendDeadlineNotifications(List conferences) { + Date now = DateUtils.addDays(new Date(), DAYS_TO_DEADLINE_NOTIFICATION); + conferences + .stream() + .filter(conference -> needToSendDeadlineNotification(conference, now)) + .forEach(this::sendMessageDeadline); + } + + private boolean needToSendDeadlineNotification(Conference conference, Date compareDate) { + return (conference.getNextDeadline().isPresent()) + && conference.getNextDeadline().get().getDate().after(new Date()) + && conference.getNextDeadline().get().getDate().before(compareDate); + } + + private void sendMessageDeadline(Conference conference) { + Map variables = ImmutableMap.of("conference", conference); + sendForAllParticipants(variables, conference, TEMPLATE_DEADLINE, String.format(TITLE_DEADLINE, conference.getTitle())); + } + + public void sendCreateNotification(Conference conference) { + Map variables = ImmutableMap.of("conference", conference); + sendForAllUsers(variables, TEMPLATE_CREATE, String.format(TITLE_CREATE, conference.getTitle())); + } + + public void updateDeadlineNotification(Conference conference) { + Map variables = ImmutableMap.of("conference", conference); + sendForAllParticipants(variables, conference, TEMPLATE_UPDATE_DEADLINES, String.format(TITLE_UPDATE_DEADLINES, conference.getTitle())); + } + + public void updateConferencesDatesNotification(Conference conference, Date oldBeginDate, Date oldEndDate) { + Map variables = ImmutableMap.of("conference", conference, "oldBeginDate", oldBeginDate, "oldEndDate", oldEndDate); + sendForAllParticipants(variables, conference, TEMPLATE_UPDATE_DATES, String.format(TITLE_UPDATE_DATES, conference.getTitle())); + } + + private void sendForAllUsers(Map variables, String template, String title) { + userService.findAll().forEach(user -> mailService.sendEmailFromTemplate(variables, user, template, title)); + } + + private void sendForAllParticipants(Map variables, Conference conference, String template, String title) { + conference.getUsers().forEach(conferenceUser -> mailService.sendEmailFromTemplate(variables, conferenceUser.getUser(), template, title)); + } + + + public void sendPingNotifications(List conferences) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + calendar.add(Calendar.DAY_OF_MONTH, YESTERDAY); + conferences + .stream() + .filter(conference -> { + Integer pingCount = pingService.countPingYesterday(conference, calendar); + return needToSendPingNotification(conference, pingCount); + }) + .forEach(this::sendMessagePing); + } + + private boolean needToSendPingNotification(Conference conference, Integer pingCount) { + if (pingCount > 0) { + conference.setPing((Integer) pingCount); + return true; + } + return false; + } + + private void sendMessagePing(Conference conference) { + Map variables = ImmutableMap.of("conference", conference); + sendForAllParticipants(variables, conference, TEMPLATE_PING, String.format(TITLE_PING, conference.getTitle())); + } } diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceScheduler.java b/src/main/java/ru/ulstu/conference/service/ConferenceScheduler.java new file mode 100644 index 0000000..6f8fe90 --- /dev/null +++ b/src/main/java/ru/ulstu/conference/service/ConferenceScheduler.java @@ -0,0 +1,37 @@ +package ru.ulstu.conference.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +public class ConferenceScheduler { + private final static boolean IS_DEADLINE_NOTIFICATION_BEFORE_WEEK = true; + + private final Logger log = LoggerFactory.getLogger(ConferenceScheduler.class); + + private final ConferenceNotificationService conferenceNotificationService; + private final ConferenceService conferenceService; + + public ConferenceScheduler(ConferenceNotificationService conferenceNotificationService, + ConferenceService conferenceService) { + this.conferenceNotificationService = conferenceNotificationService; + this.conferenceService = conferenceService; + } + + + @Scheduled(cron = "0 0 8 * * MON", zone = "Europe/Samara") + public void checkDeadlineBeforeWeek() { + log.debug("ConferenceScheduler.checkDeadlineBeforeWeek started"); + conferenceNotificationService.sendDeadlineNotifications(conferenceService.findAll()); + log.debug("ConferenceScheduler.checkDeadlineBeforeWeek finished"); + } + + @Scheduled(cron = "0 0 8 * * *", zone = "Europe/Samara") + public void checkNewPing() { + log.debug("ConferenceScheduler.checkPing started"); + conferenceNotificationService.sendPingNotifications(conferenceService.findAll()); + log.debug("ConferenceScheduler.checkPing finished"); + } +} diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceService.java b/src/main/java/ru/ulstu/conference/service/ConferenceService.java index 905c208..38de435 100644 --- a/src/main/java/ru/ulstu/conference/service/ConferenceService.java +++ b/src/main/java/ru/ulstu/conference/service/ConferenceService.java @@ -5,15 +5,18 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.ModelMap; +import org.springframework.validation.Errors; import ru.ulstu.conference.model.Conference; import ru.ulstu.conference.model.ConferenceDto; import ru.ulstu.conference.model.ConferenceFilterDto; import ru.ulstu.conference.model.ConferenceUser; import ru.ulstu.conference.repository.ConferenceRepository; +import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.service.DeadlineService; import ru.ulstu.paper.model.Paper; import ru.ulstu.paper.service.PaperService; import ru.ulstu.ping.service.PingService; +import ru.ulstu.timeline.service.EventService; import ru.ulstu.user.model.User; import ru.ulstu.user.service.UserService; @@ -22,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import static org.springframework.util.ObjectUtils.isEmpty; import static ru.ulstu.core.util.StreamApiUtils.convert; @@ -36,19 +40,25 @@ public class ConferenceService { private final PaperService paperService; private final UserService userService; private final PingService pingService; + private final ConferenceNotificationService conferenceNotificationService; + private final EventService eventService; public ConferenceService(ConferenceRepository conferenceRepository, ConferenceUserService conferenceUserService, DeadlineService deadlineService, PaperService paperService, UserService userService, - PingService pingService) { + PingService pingService, + ConferenceNotificationService conferenceNotificationService, + EventService eventService) { this.conferenceRepository = conferenceRepository; this.conferenceUserService = conferenceUserService; this.deadlineService = deadlineService; this.paperService = paperService; this.userService = userService; this.pingService = pingService; + this.conferenceNotificationService = conferenceNotificationService; + this.eventService = eventService; } public ConferenceDto getExistConferenceById(Integer id) { @@ -91,13 +101,25 @@ public class ConferenceService { public Integer create(ConferenceDto conferenceDto) throws IOException { Conference newConference = copyFromDto(new Conference(), conferenceDto); newConference = conferenceRepository.save(newConference); + conferenceNotificationService.sendCreateNotification(newConference); + eventService.createFromConference(newConference); return newConference.getId(); } @Transactional public Integer update(ConferenceDto conferenceDto) throws IOException { Conference conference = conferenceRepository.findOne(conferenceDto.getId()); + List oldDeadlines = conference.getDeadlines().stream() + .map(this::copyDeadline) + .collect(Collectors.toList()); + Date oldBeginDate = conference.getBeginDate(); + Date oldEndDate = conference.getEndDate(); conferenceRepository.save(copyFromDto(conference, conferenceDto)); + eventService.updateConferenceDeadlines(conference); + sendNotificationAfterUpdateDeadlines(conference, oldDeadlines); + if (!conference.getBeginDate().equals(oldBeginDate) || !conference.getEndDate().equals(oldEndDate)) { + conferenceNotificationService.updateConferencesDatesNotification(conference, oldBeginDate, oldEndDate); + } conferenceDto.getRemovedDeadlineIds().forEach(deadlineService::remove); return conference.getId(); } @@ -109,6 +131,11 @@ public class ConferenceService { } } + public void addDeadline(ConferenceDto conferenceDto) { + conferenceDto.getDeadlines().add(new Deadline()); + } + + public void removeDeadline(ConferenceDto conferenceDto, Integer deadlineIndex) throws IOException { if (conferenceDto.getDeadlines().get(deadlineIndex).getId() != null) { conferenceDto.getRemovedDeadlineIds().add(conferenceDto.getDeadlines().get(deadlineIndex).getId()); @@ -224,4 +251,39 @@ public class ConferenceService { modelMap.addAttribute("nearshoreSales", nearshoreSales); modelMap.addAttribute("offshoreSales", offshoreSales); } + + public void sendNotificationAfterUpdateDeadlines(Conference conference, List oldDeadlines) { + if (oldDeadlines.size() != conference.getDeadlines().size()) { + conferenceNotificationService.updateDeadlineNotification(conference); + return; + } + + if (conference.getDeadlines() + .stream() + .filter(deadline -> !oldDeadlines.contains(deadline)) + .count() > 0) { + conferenceNotificationService.updateDeadlineNotification(conference); + } + } + + public Deadline copyDeadline(Deadline oldDeadline) { + Deadline newDeadline = new Deadline(oldDeadline.getDate(), oldDeadline.getDescription()); + newDeadline.setId(oldDeadline.getId()); + return newDeadline; + } + + public void checkEmptyFieldsOfDeadline(ConferenceDto conferenceDto, Errors errors) { + for (Deadline deadline : conferenceDto.getDeadlines()) { + if (deadline.getDate() == null || deadline.getDescription().isEmpty()) { + errors.rejectValue("deadlines", "errorCode", "Все поля дедлайна должны быть заполнены"); + } + } + } + + + public void filterEmptyDeadlines(ConferenceDto conferenceDto) { + conferenceDto.setDeadlines(conferenceDto.getDeadlines().stream() + .filter(dto -> dto.getDate() != null || !org.springframework.util.StringUtils.isEmpty(dto.getDescription())) + .collect(Collectors.toList())); + } } diff --git a/src/main/java/ru/ulstu/deadline/model/Deadline.java b/src/main/java/ru/ulstu/deadline/model/Deadline.java index 404e5c8..148697c 100644 --- a/src/main/java/ru/ulstu/deadline/model/Deadline.java +++ b/src/main/java/ru/ulstu/deadline/model/Deadline.java @@ -9,6 +9,7 @@ import javax.persistence.Entity; import javax.persistence.Temporal; import javax.persistence.TemporalType; import java.util.Date; +import java.util.Objects; @Entity public class Deadline extends BaseEntity { @@ -51,4 +52,26 @@ public class Deadline extends BaseEntity { public void setDate(Date date) { this.date = date; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + Deadline deadline = (Deadline) o; + return getId().equals(deadline.getId()) && + description.equals(deadline.description) && + date.equals(deadline.date); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), description, date); + } } diff --git a/src/main/java/ru/ulstu/grant/controller/GrantController.java b/src/main/java/ru/ulstu/grant/controller/GrantController.java index 85a0cd9..b28f8c0 100644 --- a/src/main/java/ru/ulstu/grant/controller/GrantController.java +++ b/src/main/java/ru/ulstu/grant/controller/GrantController.java @@ -132,7 +132,7 @@ public class GrantController { @ModelAttribute("allPapers") public List getAllPapers() { - return grantService.getAllPapers(); + return grantService.getAllUncompletedPapers(); } private void filterEmptyDeadlines(GrantDto grantDto) { diff --git a/src/main/java/ru/ulstu/grant/model/Grant.java b/src/main/java/ru/ulstu/grant/model/Grant.java index abd61ac..760895e 100644 --- a/src/main/java/ru/ulstu/grant/model/Grant.java +++ b/src/main/java/ru/ulstu/grant/model/Grant.java @@ -1,5 +1,7 @@ package ru.ulstu.grant.model; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; import org.hibernate.validator.constraints.NotBlank; import ru.ulstu.core.model.BaseEntity; import ru.ulstu.core.model.UserContainer; @@ -7,6 +9,7 @@ import ru.ulstu.deadline.model.Deadline; import ru.ulstu.file.model.FileData; import ru.ulstu.paper.model.Paper; import ru.ulstu.project.model.Project; +import ru.ulstu.timeline.model.Event; import ru.ulstu.user.model.User; import javax.persistence.CascadeType; @@ -66,10 +69,11 @@ public class Grant extends BaseEntity implements UserContainer { private String comment; - //Заявка на грант - @ManyToOne - @JoinColumn(name = "file_id") - private FileData application; + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "grant_id", unique = true) + @Fetch(FetchMode.SUBSELECT) + private List files = new ArrayList<>(); + @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "project_id") @@ -87,8 +91,13 @@ public class Grant extends BaseEntity implements UserContainer { @JoinTable(name = "grants_papers", joinColumns = {@JoinColumn(name = "grant_id")}, inverseJoinColumns = {@JoinColumn(name = "paper_id")}) + @Fetch(FetchMode.SUBSELECT) private List papers = new ArrayList<>(); + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "grant_id") + private List events = new ArrayList<>(); + public GrantStatus getStatus() { return status; } @@ -113,12 +122,12 @@ public class Grant extends BaseEntity implements UserContainer { this.comment = comment; } - public FileData getApplication() { - return application; + public List getFiles() { + return files; } - public void setApplication(FileData application) { - this.application = application; + public void setFiles(List files) { + this.files = files; } public String getTitle() { @@ -166,6 +175,14 @@ public class Grant extends BaseEntity implements UserContainer { this.papers = papers; } + public List getEvents() { + return events; + } + + public void setEvents(List events) { + this.events = events; + } + public Optional getNextDeadline() { return deadlines .stream() diff --git a/src/main/java/ru/ulstu/grant/model/GrantDto.java b/src/main/java/ru/ulstu/grant/model/GrantDto.java index 3fb77c5..e842b10 100644 --- a/src/main/java/ru/ulstu/grant/model/GrantDto.java +++ b/src/main/java/ru/ulstu/grant/model/GrantDto.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.constraints.NotEmpty; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.file.model.FileDataDto; import ru.ulstu.paper.model.Paper; import ru.ulstu.project.model.ProjectDto; import ru.ulstu.user.model.UserDto; @@ -25,7 +26,7 @@ public class GrantDto { private Grant.GrantStatus status; private List deadlines = new ArrayList<>(); private String comment; - private String applicationFileName; + private List files = new ArrayList<>(); private ProjectDto project; private Set authorIds; private Set authors; @@ -33,6 +34,8 @@ public class GrantDto { private boolean wasLeader; private boolean hasAge; private boolean hasDegree; + private boolean hasBAKPapers; + private boolean hasScopusPapers; private List paperIds = new ArrayList<>(); private List papers = new ArrayList<>(); private List removedDeadlineIds = new ArrayList<>(); @@ -47,6 +50,7 @@ public class GrantDto { @JsonProperty("status") Grant.GrantStatus status, @JsonProperty("deadlines") List deadlines, @JsonProperty("comment") String comment, + @JsonProperty("files") List files, @JsonProperty("project") ProjectDto project, @JsonProperty("authorIds") Set authorIds, @JsonProperty("authors") Set authors, @@ -61,8 +65,9 @@ public class GrantDto { this.status = status; this.deadlines = deadlines; this.comment = comment; - this.applicationFileName = null; + this.files = files; this.project = project; + this.authorIds = authorIds; this.authors = authors; this.leaderId = leaderId; this.wasLeader = wasLeader; @@ -78,8 +83,8 @@ public class GrantDto { this.status = grant.getStatus(); this.deadlines = grant.getDeadlines(); this.comment = grant.getComment(); + this.files = convert(grant.getFiles(), FileDataDto::new); this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject()); - 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(); @@ -130,20 +135,20 @@ public class GrantDto { this.comment = comment; } - public ProjectDto getProject() { - return project; + public List getFiles() { + return files; } - public void setProject(ProjectDto project) { - this.project = project; + public void setFiles(List files) { + this.files = files; } - public String getApplicationFileName() { - return applicationFileName; + public ProjectDto getProject() { + return project; } - public void setApplicationFileName(String applicationFileName) { - this.applicationFileName = applicationFileName; + public void setProject(ProjectDto project) { + this.project = project; } public Set getAuthorIds() { @@ -224,4 +229,20 @@ public class GrantDto { public void setRemovedDeadlineIds(List removedDeadlineIds) { this.removedDeadlineIds = removedDeadlineIds; } + + public boolean isHasBAKPapers() { + return hasBAKPapers; + } + + public void setHasBAKPapers(boolean hasBAKPapers) { + this.hasBAKPapers = hasBAKPapers; + } + + public boolean isHasScopusPapers() { + return hasScopusPapers; + } + + public void setHasScopusPapers(boolean hasScopusPapers) { + this.hasScopusPapers = hasScopusPapers; + } } diff --git a/src/main/java/ru/ulstu/grant/service/GrantNotificationService.java b/src/main/java/ru/ulstu/grant/service/GrantNotificationService.java new file mode 100644 index 0000000..677b38f --- /dev/null +++ b/src/main/java/ru/ulstu/grant/service/GrantNotificationService.java @@ -0,0 +1,73 @@ +package ru.ulstu.grant.service; + +import com.google.common.collect.ImmutableMap; +import org.springframework.stereotype.Service; +import ru.ulstu.core.util.DateUtils; +import ru.ulstu.grant.model.Grant; +import ru.ulstu.user.model.User; +import ru.ulstu.user.service.MailService; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Service +public class GrantNotificationService { + private final static int DAYS_TO_DEADLINE_NOTIFICATION = 7; + private final static String TEMPLATE_DEADLINE = "grantDeadlineNotification"; + private final static String TEMPLATE_CREATE = "grantCreateNotification"; + private final static String TEMPLATE_AUTHORS_CHANGED = "grantAuthorsChangeNotification"; + private final static String TEMPLATE_LEADER_CHANGED = "grantLeaderChangeNotification"; + + private final static String TITLE_DEADLINE = "Приближается дедлайн гранта: %s"; + private final static String TITLE_CREATE = "Создан грант: %s"; + private final static String TITLE_AUTHORS_CHANGED = "Изменился состав рабочей группы гранта: %s"; + private final static String TITLE_LEADER_CHANGED = "Изменился руководитель гранта: %s"; + + private final MailService mailService; + + public GrantNotificationService(MailService mailService) { + this.mailService = mailService; + } + + public void sendDeadlineNotifications(List grants, boolean isDeadlineBeforeWeek) { + Date now = DateUtils.addDays(new Date(), DAYS_TO_DEADLINE_NOTIFICATION); + grants.stream() + .filter(grant -> needToSendDeadlineNotification(grant, now, isDeadlineBeforeWeek)) + .forEach(grant -> sendMessageDeadline(grant)); + } + + private boolean needToSendDeadlineNotification(Grant grant, Date compareDate, boolean isDeadlineBeforeWeek) { + return (grant.getNextDeadline().isPresent()) + && (compareDate.before(grant.getNextDeadline().get().getDate()) && isDeadlineBeforeWeek + || compareDate.after(grant.getNextDeadline().get().getDate()) && !isDeadlineBeforeWeek) + && grant.getNextDeadline().get().getDate().after(new Date()); + } + + private void sendMessageDeadline(Grant grant) { + Map variables = ImmutableMap.of("grant", grant); + sendForAllAuthors(variables, grant, TEMPLATE_DEADLINE, String.format(TITLE_DEADLINE, grant.getTitle())); + } + + public void sendCreateNotification(Grant grant) { + Map variables = ImmutableMap.of("grant", grant); + sendForAllAuthors(variables, grant, TEMPLATE_CREATE, String.format(TITLE_CREATE, grant.getTitle())); + } + + public void sendAuthorsChangeNotification(Grant grant, Set oldAuthors) { + Map variables = ImmutableMap.of("grant", grant, "oldAuthors", oldAuthors); + sendForAllAuthors(variables, grant, TEMPLATE_AUTHORS_CHANGED, String.format(TITLE_AUTHORS_CHANGED, grant.getTitle())); + } + + public void sendLeaderChangeNotification(Grant grant, User oldLeader) { + Map variables = ImmutableMap.of("grant", grant, "oldLeader", oldLeader); + sendForAllAuthors(variables, grant, TEMPLATE_LEADER_CHANGED, String.format(TITLE_LEADER_CHANGED, grant.getTitle())); + } + + private void sendForAllAuthors(Map variables, Grant grant, String template, String title) { + Set allAuthors = grant.getAuthors(); + allAuthors.forEach(author -> mailService.sendEmailFromTemplate(variables, author, template, title)); + mailService.sendEmailFromTemplate(variables, grant.getLeader(), template, title); + } +} diff --git a/src/main/java/ru/ulstu/grant/service/GrantScheduler.java b/src/main/java/ru/ulstu/grant/service/GrantScheduler.java new file mode 100644 index 0000000..1c38c4a --- /dev/null +++ b/src/main/java/ru/ulstu/grant/service/GrantScheduler.java @@ -0,0 +1,30 @@ +package ru.ulstu.grant.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +public class GrantScheduler { + private final static boolean IS_DEADLINE_NOTIFICATION_BEFORE_WEEK = true; + + private final Logger log = LoggerFactory.getLogger(GrantScheduler.class); + + private final GrantNotificationService grantNotificationService; + private final GrantService grantService; + + public GrantScheduler(GrantNotificationService grantNotificationService, + GrantService grantService) { + this.grantNotificationService = grantNotificationService; + this.grantService = grantService; + } + + + @Scheduled(cron = "0 0 8 * * MON", zone = "Europe/Samara") + public void checkDeadlineBeforeWeek() { + log.debug("GrantScheduler.checkDeadlineBeforeWeek started"); + grantNotificationService.sendDeadlineNotifications(grantService.findAll(), IS_DEADLINE_NOTIFICATION_BEFORE_WEEK); + log.debug("GrantScheduler.checkDeadlineBeforeWeek finished"); + } +} diff --git a/src/main/java/ru/ulstu/grant/service/GrantService.java b/src/main/java/ru/ulstu/grant/service/GrantService.java index cabed80..83ef49e 100644 --- a/src/main/java/ru/ulstu/grant/service/GrantService.java +++ b/src/main/java/ru/ulstu/grant/service/GrantService.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.ulstu.deadline.model.Deadline; import ru.ulstu.deadline.service.DeadlineService; +import ru.ulstu.file.model.FileDataDto; import ru.ulstu.file.service.FileService; import ru.ulstu.grant.model.Grant; import ru.ulstu.grant.model.GrantDto; @@ -14,15 +15,19 @@ import ru.ulstu.paper.service.PaperService; import ru.ulstu.project.model.Project; import ru.ulstu.project.model.ProjectDto; import ru.ulstu.project.service.ProjectService; +import ru.ulstu.timeline.service.EventService; import ru.ulstu.user.model.User; import ru.ulstu.user.service.UserService; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; +import java.util.Set; +import static java.util.stream.Collectors.toList; import static org.springframework.util.ObjectUtils.isEmpty; import static ru.ulstu.core.util.StreamApiUtils.convert; import static ru.ulstu.grant.model.Grant.GrantStatus.APPLICATION; @@ -37,19 +42,25 @@ public class GrantService { private final FileService fileService; private final UserService userService; private final PaperService paperService; + private final EventService eventService; + private final GrantNotificationService grantNotificationService; public GrantService(GrantRepository grantRepository, FileService fileService, DeadlineService deadlineService, ProjectService projectService, UserService userService, - PaperService paperService) { + PaperService paperService, + EventService eventService, + GrantNotificationService grantNotificationService) { this.grantRepository = grantRepository; this.fileService = fileService; this.deadlineService = deadlineService; this.projectService = projectService; this.userService = userService; this.paperService = paperService; + this.eventService = eventService; + this.grantNotificationService = grantNotificationService; } public List findAll() { @@ -70,6 +81,8 @@ public class GrantService { public Integer create(GrantDto grantDto) throws IOException { Grant newGrant = copyFromDto(new Grant(), grantDto); newGrant = grantRepository.save(newGrant); + eventService.createFromGrant(newGrant); + grantNotificationService.sendCreateNotification(newGrant); return newGrant.getId(); } @@ -81,9 +94,9 @@ public class GrantService { grant.setProject(projectService.findById(grantDto.getProject().getId())); } grant.setDeadlines(deadlineService.saveOrCreate(grantDto.getDeadlines())); - if (grantDto.getApplicationFileName() != null) { - grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName())); - } + grant.setFiles(fileService.saveOrCreate(grantDto.getFiles().stream() + .filter(f -> !f.isDeleted()) + .collect(toList()))); grant.getAuthors().clear(); if (grantDto.getAuthorIds() != null && !grantDto.getAuthorIds().isEmpty()) { grantDto.getAuthorIds().forEach(authorIds -> grant.getAuthors().add(userService.findById(authorIds))); @@ -106,20 +119,36 @@ public class GrantService { @Transactional public Integer update(GrantDto grantDto) throws IOException { Grant grant = grantRepository.findOne(grantDto.getId()); - if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) { - fileService.deleteFile(grant.getApplication()); + Set oldAuthors = new HashSet<>(grant.getAuthors()); + User oldLeader = grant.getLeader(); + for (FileDataDto file : grantDto.getFiles().stream() + .filter(f -> f.isDeleted() && f.getId() != null) + .collect(toList())) { + fileService.delete(file.getId()); } grantDto.getRemovedDeadlineIds().forEach(deadlineService::remove); grantRepository.save(copyFromDto(grant, grantDto)); + + grant.getAuthors().forEach(author -> { + if (!oldAuthors.contains(author)) { + grantNotificationService.sendAuthorsChangeNotification(grant, oldAuthors); + } + }); + oldAuthors.forEach(oldAuthor -> { + if (!grant.getAuthors().contains(oldAuthor)) { + grantNotificationService.sendAuthorsChangeNotification(grant, oldAuthors); + } + }); + if (grant.getLeader() != oldLeader) { + grantNotificationService.sendLeaderChangeNotification(grant, oldLeader); + } + eventService.updateGrantDeadlines(grant); return grant.getId(); } @Transactional public void delete(Integer grantId) throws IOException { Grant grant = grantRepository.findOne(grantId); - if (grant.getApplication() != null) { - fileService.deleteFile(grant.getApplication()); - } grantRepository.delete(grant); } @@ -139,6 +168,10 @@ public class GrantService { grant.setLeader(user); grant.getPapers().add(paper); grant = grantRepository.save(grant); + + eventService.createFromGrant(grant); + grantNotificationService.sendCreateNotification(grant); + return grant; } @@ -153,19 +186,28 @@ public class GrantService { public List getGrantAuthors(GrantDto grantDto) { List filteredUsers = userService.filterByAgeAndDegree(grantDto.isHasAge(), grantDto.isHasDegree()); if (grantDto.isWasLeader()) { - filteredUsers = filteredUsers - .stream() - .filter(getCompletedGrantLeaders()::contains) - .collect(Collectors.toList()); + filteredUsers = checkContains(filteredUsers, getCompletedGrantLeaders()); + } + if (grantDto.isHasBAKPapers()) { + filteredUsers = checkContains(filteredUsers, getBAKAuthors()); + } + if (grantDto.isHasScopusPapers()) { + filteredUsers = checkContains(filteredUsers, getScopusAuthors()); } return filteredUsers; } + private List checkContains(List filteredUsers, List checkUsers) { + return filteredUsers.stream() + .filter(checkUsers::contains) + .collect(toList()); + } + private List getCompletedGrantLeaders() { return grantRepository.findByStatus(Grant.GrantStatus.COMPLETED) .stream() .map(Grant::getLeader) - .collect(Collectors.toList()); + .collect(toList()); } public List getGrantPapers(List paperIds) { @@ -177,6 +219,10 @@ public class GrantService { return paperService.findAll(); } + public List getAllUncompletedPapers() { + return paperService.findAllNotCompleted(); + } + public void attachPaper(GrantDto grantDto) { if (!grantDto.getPaperIds().isEmpty()) { grantDto.getPapers().clear(); @@ -193,4 +239,26 @@ public class GrantService { grantDto.getDeadlines().remove((int) deadlineId); } + private List getCompletedPapersAuthors(Paper.PaperType type) { + List papers = paperService.findAllCompletedByType(type); + return papers.stream() + .filter(paper -> paper.getAuthors() != null) + .flatMap(paper -> paper.getAuthors().stream()) + .collect(toList()); + } + + private List getBAKAuthors() { + return getCompletedPapersAuthors(Paper.PaperType.VAK) + .stream() + .distinct() + .collect(toList()); + } + + private List getScopusAuthors() { + List authors = getCompletedPapersAuthors(Paper.PaperType.SCOPUS); + return authors + .stream() + .filter(author -> Collections.frequency(authors, author) > 3) + .collect(toList()); + } } diff --git a/src/main/java/ru/ulstu/paper/model/Paper.java b/src/main/java/ru/ulstu/paper/model/Paper.java index 65bd32c..4536ce5 100644 --- a/src/main/java/ru/ulstu/paper/model/Paper.java +++ b/src/main/java/ru/ulstu/paper/model/Paper.java @@ -3,10 +3,12 @@ package ru.ulstu.paper.model; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.validator.constraints.NotBlank; +import ru.ulstu.conference.model.Conference; import ru.ulstu.core.model.BaseEntity; import ru.ulstu.core.model.UserContainer; import ru.ulstu.deadline.model.Deadline; import ru.ulstu.file.model.FileData; +import ru.ulstu.grant.model.Grant; import ru.ulstu.timeline.model.Event; import ru.ulstu.user.model.User; @@ -114,6 +116,12 @@ public class Paper extends BaseEntity implements UserContainer { @Column(name = "latex_text") private String latexText; + @ManyToMany(mappedBy = "papers") + private List conferences; + + @ManyToMany(mappedBy = "papers") + private List grants; + public PaperStatus getStatus() { return status; } @@ -218,6 +226,22 @@ public class Paper extends BaseEntity implements UserContainer { this.latexText = latexText; } + public List getConferences() { + return conferences; + } + + public void setConferences(List conferences) { + this.conferences = conferences; + } + + public List getGrants() { + return grants; + } + + public void setGrants(List grants) { + this.grants = grants; + } + @Override public Set getUsers() { return getAuthors(); diff --git a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java index cab9b1a..42d3703 100644 --- a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java +++ b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java @@ -16,4 +16,12 @@ public interface PaperRepository extends JpaRepository { List findByIdNotIn(List paperIds); List findAllByIdIn(List paperIds); + + List findByTypeAndStatus(Paper.PaperType type, Paper.PaperStatus status); + + List findByStatusNot(Paper.PaperStatus status); + + List findByConferencesIsNullAndStatusNot(Paper.PaperStatus status); + + List findByIdNotInAndConferencesIsNullAndStatusNot(List paperIds, Paper.PaperStatus status); } diff --git a/src/main/java/ru/ulstu/paper/service/PaperService.java b/src/main/java/ru/ulstu/paper/service/PaperService.java index 3db7c84..0083de7 100644 --- a/src/main/java/ru/ulstu/paper/service/PaperService.java +++ b/src/main/java/ru/ulstu/paper/service/PaperService.java @@ -242,11 +242,14 @@ public class PaperService { public List findAllNotSelect(List paperIds) { if (!paperIds.isEmpty()) { - return sortPapers(paperRepository.findByIdNotIn(paperIds)); + return sortPapers(paperRepository.findByIdNotInAndConferencesIsNullAndStatusNot(paperIds, COMPLETED)); } else { - return sortPapers(paperRepository.findAll()); + return sortPapers(paperRepository.findByConferencesIsNullAndStatusNot(COMPLETED)); } + } + public List findAllNotCompleted() { + return paperRepository.findByStatusNot(COMPLETED); } public List findAllSelect(List paperIds) { @@ -303,4 +306,8 @@ public class PaperService { StringUtils.isEmpty(referenceDto.getPublisher()) ? "" : referenceDto.getPublisher() + ", ", referenceDto.getPages()); } + + public List findAllCompletedByType(Paper.PaperType type) { + return paperRepository.findByTypeAndStatus(type, Paper.PaperStatus.COMPLETED); + } } diff --git a/src/main/java/ru/ulstu/ping/repository/PingRepository.java b/src/main/java/ru/ulstu/ping/repository/PingRepository.java index ebafc0c..de79dd7 100644 --- a/src/main/java/ru/ulstu/ping/repository/PingRepository.java +++ b/src/main/java/ru/ulstu/ping/repository/PingRepository.java @@ -1,7 +1,13 @@ package ru.ulstu.ping.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import ru.ulstu.conference.model.Conference; import ru.ulstu.ping.model.Ping; public interface PingRepository extends JpaRepository { + + @Query("SELECT count(*) FROM Ping p WHERE (DAY(p.date) = :day) AND (MONTH(p.date) = :month) AND (YEAR(p.date) = :year) AND (p.conference = :conference)") + long countByConferenceAndDate(@Param("conference") Conference conference, @Param("day") Integer day, @Param("month") Integer month, @Param("year") Integer year); } diff --git a/src/main/java/ru/ulstu/ping/service/PingService.java b/src/main/java/ru/ulstu/ping/service/PingService.java index ff0d249..f7156f0 100644 --- a/src/main/java/ru/ulstu/ping/service/PingService.java +++ b/src/main/java/ru/ulstu/ping/service/PingService.java @@ -8,6 +8,7 @@ import ru.ulstu.ping.repository.PingRepository; import ru.ulstu.user.service.UserService; import java.io.IOException; +import java.util.Calendar; import java.util.Date; @Service @@ -27,4 +28,9 @@ public class PingService { newPing.setConference(conference); pingRepository.save(newPing); } + + public Integer countPingYesterday(Conference conference, Calendar calendar) { + return Math.toIntExact(pingRepository.countByConferenceAndDate(conference, calendar.get(Calendar.DAY_OF_MONTH), + calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.YEAR))); + } } diff --git a/src/main/java/ru/ulstu/timeline/model/Event.java b/src/main/java/ru/ulstu/timeline/model/Event.java index ef0a4b8..9252cec 100644 --- a/src/main/java/ru/ulstu/timeline/model/Event.java +++ b/src/main/java/ru/ulstu/timeline/model/Event.java @@ -1,7 +1,9 @@ package ru.ulstu.timeline.model; import org.hibernate.validator.constraints.NotBlank; +import ru.ulstu.conference.model.Conference; import ru.ulstu.core.model.BaseEntity; +import ru.ulstu.grant.model.Grant; import ru.ulstu.paper.model.Paper; import ru.ulstu.user.model.User; @@ -18,6 +20,7 @@ import javax.persistence.OneToMany; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.validation.constraints.NotNull; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -62,7 +65,7 @@ public class Event extends BaseEntity { private String description; @ManyToMany(fetch = FetchType.EAGER) - private List recipients; + private List recipients = new ArrayList(); @ManyToOne @JoinColumn(name = "child_id") @@ -76,6 +79,14 @@ public class Event extends BaseEntity { @JoinColumn(name = "paper_id") private Paper paper; + @ManyToOne + @JoinColumn(name = "conference_id") + private Conference conference; + + @ManyToOne + @JoinColumn(name = "grant_id") + private Grant grant; + public String getTitle() { return title; } @@ -163,4 +174,20 @@ public class Event extends BaseEntity { public void setPaper(Paper paper) { this.paper = paper; } + + public Conference getConference() { + return conference; + } + + public void setConference(Conference conference) { + this.conference = conference; + } + + public Grant getGrant() { + return grant; + } + + public void setGrant(Grant grant) { + this.grant = grant; + } } diff --git a/src/main/java/ru/ulstu/timeline/model/EventDto.java b/src/main/java/ru/ulstu/timeline/model/EventDto.java index 6a4a90b..89df5d8 100644 --- a/src/main/java/ru/ulstu/timeline/model/EventDto.java +++ b/src/main/java/ru/ulstu/timeline/model/EventDto.java @@ -3,6 +3,8 @@ package ru.ulstu.timeline.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.NotBlank; +import ru.ulstu.conference.model.ConferenceDto; +import ru.ulstu.grant.model.GrantDto; import ru.ulstu.paper.model.PaperDto; import ru.ulstu.user.model.UserDto; @@ -25,6 +27,8 @@ public class EventDto { private final String description; private final List recipients; private PaperDto paperDto; + private ConferenceDto conferenceDto; + private GrantDto grantDto; @JsonCreator public EventDto(@JsonProperty("id") Integer id, @@ -36,7 +40,9 @@ public class EventDto { @JsonProperty("updateDate") Date updateDate, @JsonProperty("description") String description, @JsonProperty("paperDto") PaperDto paperDto, - @JsonProperty("recipients") List recipients) { + @JsonProperty("recipients") List recipients, + @JsonProperty("conferenceDto") ConferenceDto conferenceDto, + @JsonProperty("grantDto") GrantDto grantDto) { this.id = id; this.title = title; this.period = period; @@ -47,6 +53,8 @@ public class EventDto { this.description = description; this.recipients = recipients; this.paperDto = paperDto; + this.conferenceDto = conferenceDto; + this.grantDto = grantDto; } public EventDto(Event event) { @@ -58,8 +66,16 @@ public class EventDto { this.createDate = event.getCreateDate(); this.updateDate = event.getUpdateDate(); this.description = event.getDescription(); - this.paperDto = new PaperDto(event.getPaper()); this.recipients = convert(event.getRecipients(), UserDto::new); + if (paperDto != null) { + this.paperDto = new PaperDto(event.getPaper()); + } + if (conferenceDto != null) { + this.conferenceDto = new ConferenceDto(event.getConference()); + } + if (grantDto != null) { + this.grantDto = new GrantDto(event.getGrant()); + } } public Integer getId() { @@ -105,4 +121,20 @@ public class EventDto { public void setPaperDto(PaperDto paperDto) { this.paperDto = paperDto; } + + public ConferenceDto getConferenceDto() { + return conferenceDto; + } + + public void setConferenceDto(ConferenceDto conferenceDto) { + this.conferenceDto = conferenceDto; + } + + public GrantDto getGrantDto() { + return grantDto; + } + + public void setGrantDto(GrantDto grantDto) { + this.grantDto = grantDto; + } } diff --git a/src/main/java/ru/ulstu/timeline/repository/EventRepository.java b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java index eb5c08b..e01037a 100644 --- a/src/main/java/ru/ulstu/timeline/repository/EventRepository.java +++ b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java @@ -2,6 +2,8 @@ package ru.ulstu.timeline.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import ru.ulstu.conference.model.Conference; +import ru.ulstu.grant.model.Grant; import ru.ulstu.paper.model.Paper; import ru.ulstu.timeline.model.Event; @@ -15,4 +17,8 @@ public interface EventRepository extends JpaRepository { List findAllFuture(); List findAllByPaper(Paper paper); + + List findAllByConference(Conference conference); + + List findAllByGrant(Grant grant); } diff --git a/src/main/java/ru/ulstu/timeline/service/EventService.java b/src/main/java/ru/ulstu/timeline/service/EventService.java index 2d2b83d..b935f16 100644 --- a/src/main/java/ru/ulstu/timeline/service/EventService.java +++ b/src/main/java/ru/ulstu/timeline/service/EventService.java @@ -4,7 +4,9 @@ import org.apache.commons.lang3.time.DateUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.conference.model.Conference; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.grant.model.Grant; import ru.ulstu.paper.model.Paper; import ru.ulstu.timeline.model.Event; import ru.ulstu.timeline.model.EventDto; @@ -140,4 +142,65 @@ public class EventService { public List findAllFutureDto() { return convert(findAllFuture(), EventDto::new); } + + public void createFromConference(Conference newConference) { + List timelines = timelineService.findAll(); + Timeline timeline = timelines.isEmpty() ? new Timeline() : timelines.get(0); + + for (Deadline deadline : newConference.getDeadlines() + .stream() + .filter(d -> d.getDate().after(new Date()) || DateUtils.isSameDay(d.getDate(), new Date())) + .collect(Collectors.toList())) { + Event newEvent = new Event(); + newEvent.setTitle("Дедлайн конференции"); + newEvent.setStatus(Event.EventStatus.NEW); + newEvent.setExecuteDate(deadline.getDate()); + newEvent.setCreateDate(new Date()); + newEvent.setUpdateDate(new Date()); + newEvent.setDescription("Дедлайн '" + deadline.getDescription() + "' конференции '" + newConference.getTitle() + "'"); + newConference.getUsers().forEach(conferenceUser -> newEvent.getRecipients().add(conferenceUser.getUser())); + newEvent.setConference(newConference); + save(newEvent); + + timeline.getEvents().add(newEvent); + timelineService.save(timeline); + } + } + + public void updateConferenceDeadlines(Conference conference) { + eventRepository.delete(eventRepository.findAllByConference(conference)); + createFromConference(conference); + } + + public void createFromGrant(Grant newGrant) { + List timelines = timelineService.findAll(); + Timeline timeline = timelines.isEmpty() ? new Timeline() : timelines.get(0); + + for (Deadline deadline : newGrant.getDeadlines() + .stream() + .filter(d -> d.getDate().after(new Date()) || DateUtils.isSameDay(d.getDate(), new Date())) + .collect(Collectors.toList())) { + Event newEvent = new Event(); + newEvent.setTitle("Дедлайн гранта"); + newEvent.setStatus(Event.EventStatus.NEW); + newEvent.setExecuteDate(deadline.getDate()); + newEvent.setCreateDate(new Date()); + newEvent.setUpdateDate(new Date()); + newEvent.setDescription("Дедлайн '" + deadline.getDescription() + "' гранта '" + newGrant.getTitle() + "'"); + if (newGrant.getAuthors() != null) { + newEvent.setRecipients(new ArrayList(newGrant.getAuthors())); + } + newEvent.getRecipients().add(newGrant.getLeader()); + newEvent.setGrant(newGrant); + eventRepository.save(newEvent); + + timeline.getEvents().add(newEvent); + timelineService.save(timeline); + } + } + + public void updateGrantDeadlines(Grant grant) { + eventRepository.delete(eventRepository.findAllByGrant(grant)); + createFromGrant(grant); + } } diff --git a/src/main/resources/db/changelog-20190426_000000-schema.xml b/src/main/resources/db/changelog-20190426_000000-schema.xml new file mode 100644 index 0000000..5304032 --- /dev/null +++ b/src/main/resources/db/changelog-20190426_000000-schema.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190428_000000-schema.xml b/src/main/resources/db/changelog-20190428_000000-schema.xml new file mode 100644 index 0000000..b44691d --- /dev/null +++ b/src/main/resources/db/changelog-20190428_000000-schema.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-20190430_000000-schema.xml b/src/main/resources/db/changelog-20190430_000000-schema.xml new file mode 100644 index 0000000..58a3bc5 --- /dev/null +++ b/src/main/resources/db/changelog-20190430_000000-schema.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190505_000000-schema.xml b/src/main/resources/db/changelog-20190505_000000-schema.xml index a47476e..ad56dac 100644 --- a/src/main/resources/db/changelog-20190505_000000-schema.xml +++ b/src/main/resources/db/changelog-20190505_000000-schema.xml @@ -2,23 +2,14 @@ - - - - - - - - - - - - - - - + + + + - + diff --git a/src/main/resources/db/changelog-20190505_000001-schema.xml b/src/main/resources/db/changelog-20190505_000001-schema.xml new file mode 100644 index 0000000..1148d3a --- /dev/null +++ b/src/main/resources/db/changelog-20190505_000001-schema.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190507_000000-schema.xml b/src/main/resources/db/changelog-20190507_000000-schema.xml new file mode 100644 index 0000000..0cdb99b --- /dev/null +++ b/src/main/resources/db/changelog-20190507_000000-schema.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/main/resources/db/changelog-20190507_000001-schema.xml b/src/main/resources/db/changelog-20190507_000001-schema.xml new file mode 100644 index 0000000..c320eba --- /dev/null +++ b/src/main/resources/db/changelog-20190507_000001-schema.xml @@ -0,0 +1,14 @@ + + + + + update grants + set leader_id = + (select u.id + from users u + where u.last_name = 'Романов' AND u.first_name = 'Антон'); + + + diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index a28952a..cd5d817 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -34,5 +34,11 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/mail_templates/conferenceCreateNotification.html b/src/main/resources/mail_templates/conferenceCreateNotification.html new file mode 100644 index 0000000..055e85d --- /dev/null +++ b/src/main/resources/mail_templates/conferenceCreateNotification.html @@ -0,0 +1,30 @@ + + + + Уведомление о создании конференции + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Была создана новая конференция: " + Title". +
+ Спешите принять участие! +

+

+ Даты проведения: + + - + . +

+

+ Regards, +
+ NG-tracker. +

+ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/conferenceDeadlineNotification.html b/src/main/resources/mail_templates/conferenceDeadlineNotification.html new file mode 100644 index 0000000..77c89b1 --- /dev/null +++ b/src/main/resources/mail_templates/conferenceDeadlineNotification.html @@ -0,0 +1,29 @@ + + + + Уведомление о дедлайне конференции + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Приближается дедлайн конференции " + Title". +

+

+ Срок исполнения: . +

+ +

+ Примечание: . +

+

+ Regards, +
+ NG-tracker. +

+ + diff --git a/src/main/resources/mail_templates/conferencePingNotification.html b/src/main/resources/mail_templates/conferencePingNotification.html new file mode 100644 index 0000000..a80a3f2 --- /dev/null +++ b/src/main/resources/mail_templates/conferencePingNotification.html @@ -0,0 +1,25 @@ + + + + Обратите внимание на конференциию + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Конференция " + Title" была пропингована + раз. +
+ Обратите внимание. +

+

+ Regards, +
+ NG-tracker. +

+ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/conferenceUpdateDatesNotification.html b/src/main/resources/mail_templates/conferenceUpdateDatesNotification.html new file mode 100644 index 0000000..479336f --- /dev/null +++ b/src/main/resources/mail_templates/conferenceUpdateDatesNotification.html @@ -0,0 +1,31 @@ + + + + Уведомление об изменении дат проведения конференции + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Даты проведения конференции " + Title" изменились с
+ "oldBeginDate" + - + "oldEndDate" +
+ + на
+ "" + - + "". +

+

+ Regards, +
+ NG-tracker. +

+ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/conferenceUpdateDeadlinesNotification.html b/src/main/resources/mail_templates/conferenceUpdateDeadlinesNotification.html new file mode 100644 index 0000000..f010fb8 --- /dev/null +++ b/src/main/resources/mail_templates/conferenceUpdateDeadlinesNotification.html @@ -0,0 +1,24 @@ + + + + Уведомление об изменении дедлайнов конференции + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Дедлайны конференции " + Title" притерпели изменения. +
+ Ознакомтесь с изменениями. +

+

+ Regards, +
+ NG-tracker. +

+ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/grantAuthorsChangeNotification.html b/src/main/resources/mail_templates/grantAuthorsChangeNotification.html new file mode 100644 index 0000000..2bae4fe --- /dev/null +++ b/src/main/resources/mail_templates/grantAuthorsChangeNotification.html @@ -0,0 +1,24 @@ + + + + Уведомление об изменении состава рабочей группы + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Состав рабочей группы гранта "Title" сменился с + " oldAuthors" + на " newAuthors". +

+

+ Regards, +
+ NG-tracker. +

+ + diff --git a/src/main/resources/mail_templates/grantCreateNotification.html b/src/main/resources/mail_templates/grantCreateNotification.html new file mode 100644 index 0000000..69736cd --- /dev/null +++ b/src/main/resources/mail_templates/grantCreateNotification.html @@ -0,0 +1,28 @@ + + + + Уведомление о создании гранта + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Был добавлен новый грант: " + Title". +
+

+

+ Руководитель гранта: + + Leader. +

+

+ Regards, +
+ NG-tracker. +

+ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/grantDeadlineNotification.html b/src/main/resources/mail_templates/grantDeadlineNotification.html new file mode 100644 index 0000000..190ef67 --- /dev/null +++ b/src/main/resources/mail_templates/grantDeadlineNotification.html @@ -0,0 +1,28 @@ + + + + Уведомление о дедлайне гранта + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Приближается дедлайн гранта "Title". +

+

+ Срок исполнения: Date. +

+

+ Примечание: Description. +

+

+ Regards, +
+ NG-tracker. +

+ + diff --git a/src/main/resources/mail_templates/grantLeaderChangeNotification.html b/src/main/resources/mail_templates/grantLeaderChangeNotification.html new file mode 100644 index 0000000..c6a37e0 --- /dev/null +++ b/src/main/resources/mail_templates/grantLeaderChangeNotification.html @@ -0,0 +1,24 @@ + + + + Уведомление об изменении руководителя гранта + + + + +

+ Уважаемый(ая) Ivan Ivanov +

+

+ Руководитель гранта "Title" сменился с + "oldLeader" + на "newLeader". +

+

+ Regards, +
+ NG-tracker. +

+ + diff --git a/src/main/resources/public/css/conference.css b/src/main/resources/public/css/conference.css index 6018b9c..73a96ae 100644 --- a/src/main/resources/public/css/conference.css +++ b/src/main/resources/public/css/conference.css @@ -7,6 +7,10 @@ body { border-radius: .25rem; } +.conference-row .d-flex:hover .icon-delete { + visibility: visible; +} + .filter-option-inner-inner { color: white; } @@ -17,10 +21,20 @@ body { .conference-row .d-flex .text-decoration { text-decoration: none; + margin: 0; } .conference-row .d-flex .text-decoration:nth-child(1) { - margin-left: 10px; + margin-left: 5px; +} + +.conference-row .d-flex .icon-delete { + width: 29px; + height: 29px; + margin: auto; + border: none; + visibility: hidden; + background-color: transparent; } @@ -159,7 +173,7 @@ body { } .icon-delete:hover { - background-color: #ff0000; + background-color: #ff0000 !important; transition: background-color .15s ease-in-out; } diff --git a/src/main/resources/public/css/grant.css b/src/main/resources/public/css/grant.css index 3c0cfc8..2e6349d 100644 --- a/src/main/resources/public/css/grant.css +++ b/src/main/resources/public/css/grant.css @@ -28,4 +28,16 @@ .btn-delete-deadline { color: black; +} + +.div-file-name{ + padding-top: 6px; +} + +.div-loader { + margin-bottom: 5px; +} + +.div-row-file { + margin-bottom: 2px; } \ No newline at end of file diff --git a/src/main/resources/templates/conferences/conference.html b/src/main/resources/templates/conferences/conference.html index 2d93a5e..d5906b6 100644 --- a/src/main/resources/templates/conferences/conference.html +++ b/src/main/resources/templates/conferences/conference.html @@ -1,7 +1,7 @@ + layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html"> @@ -135,7 +135,6 @@
-
@@ -148,15 +147,13 @@ Имя статьи - - + Имя статьи - diff --git a/src/main/resources/templates/conferences/fragments/confLineFragment.html b/src/main/resources/templates/conferences/fragments/confLineFragment.html index ecd166d..21b4191 100644 --- a/src/main/resources/templates/conferences/fragments/confLineFragment.html +++ b/src/main/resources/templates/conferences/fragments/confLineFragment.html @@ -7,17 +7,13 @@
- + - - - -
diff --git a/src/main/resources/templates/grants/fragments/grantFilesListFragment.html b/src/main/resources/templates/grants/fragments/grantFilesListFragment.html new file mode 100644 index 0000000..2547c80 --- /dev/null +++ b/src/main/resources/templates/grants/fragments/grantFilesListFragment.html @@ -0,0 +1,37 @@ + + + + + + +
+ + +
+ + + + +
+ + +
+
+ + + +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/grants/grant.html b/src/main/resources/templates/grants/grant.html index 9c0736d..e357876 100644 --- a/src/main/resources/templates/grants/grant.html +++ b/src/main/resources/templates/grants/grant.html @@ -61,7 +61,7 @@
-
- +
+
-
+
+
+
-
@@ -111,14 +114,16 @@
- +
- +
@@ -196,6 +201,30 @@ Статус статьи
+
+ +
+
+
  • + + + +
  • +
    +
    +
    +
    + +
    +
  • + + + +
  • +
    +
    @@ -233,20 +262,100 @@ new FileLoader({ div: "loader", url: urlFileUpload, - maxSize: 1.5, - extensions: ["doc", "docx", "xls", "jpg", "pdf", "txt", "png"], + maxSize: -1, + extensions: [], callback: function (response) { showFeedbackMessage("Файл успешно загружен"); console.debug(response); + addNewFile(response); } }); $('.selectpicker').selectpicker(); }); /*]]>*/ + function addNewFile(fileDto) { + var fileNumber = $("#files-list div.row").length; + + var newFileRow = $("
    ") + .attr("id", 'files' + fileNumber) + .addClass("row div-row-file"); + var idInput = $("") + .attr("type", "hidden") + .attr("id", "files" + fileNumber + ".id") + .attr("value", '') + .attr("name", "files[" + fileNumber + "].id"); + newFileRow.append(idInput); + var flagInput = $("") + .attr("type", "hidden") + .attr("id", "files" + fileNumber + ".deleted") + .attr("value", "false") + .attr("name", "files[" + fileNumber + "].deleted"); + newFileRow.append(flagInput); + + var nameInput = $("") + .attr("type", "hidden") + .attr("id", "files" + fileNumber + ".name") + .attr("value", fileDto.fileName) + .attr("name", "files[" + fileNumber + "].name"); + newFileRow.append(nameInput); + + var tmpFileNameInput = $("") + .attr("type", "hidden") + .attr("id", "files" + fileNumber + ".tmpFileName") + .attr("value", fileDto.tmpFileName) + .attr("name", "files[" + fileNumber + "].tmpFileName"); + newFileRow.append(tmpFileNameInput); + + var nameDiv = $("
    ") + .addClass("col-10 div-file-name") + .append($("").text(fileDto.fileName) + .attr("href", 'javascript:void(0)') + .attr("onclick", "downloadFile('" + fileDto.tmpFileName + "',null,'" + fileDto.fileName + "')")); + newFileRow.append(nameDiv); + + var nextDiv = $("
    ") + .addClass("col-2"); + + var nextA = $("") + .addClass("btn btn-danger float-right") + .attr("onclick", "$('#files" + fileNumber + "\\\\.deleted').val('true'); $('#files" + fileNumber + "').hide();") + .append(($("").attr("aria-hidden", "true")).append($("").addClass("fa fa-times"))) + ; + nextDiv.append(nextA) + newFileRow.append(nextDiv); + $("#files-list").append(newFileRow); + } + function downloadFile(tmpName, fileId, downloadName) { + let xhr = new XMLHttpRequest(); + if (fileId != null) xhr.open('GET', urlFileDownload + fileId); + if (tmpName != null) xhr.open('GET', urlFileDownloadTmp + tmpName); + xhr.responseType = 'blob'; + var formData = new FormData(); + if (fileId != null) formData.append("file-id", fileId); + if (tmpName != null) formData.append("tmp-file-name", tmpName); + + xhr.send(formData); + + xhr.onload = function () { + if (this.status == 200) { + console.debug(this.response); + var blob = new Blob([this.response], {type: '*'}); + let a = document.createElement("a"); + a.style = "display: none"; + document.body.appendChild(a); + let url = window.URL.createObjectURL(blob); + a.href = url; + a.download = downloadName; + a.click(); + window.URL.revokeObjectURL(url); + } else { + } + } + } - -