diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2b2fe64..0effceb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,11 @@ -image: ubuntu:18.04 - -cache: - key: "$CI_PROJECT_ID" - paths: - - .gradle/ +image: romanov73/is:ng-tracker-container variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" before_script: - - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - - apt-get install openjdk-8-jdk git -y + - service postgresql stop + - service postgresql start - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null - mkdir -p ~/.ssh @@ -20,12 +15,14 @@ before_script: build: stage: build script: ./gradlew assemble - cache: - key: "$CI_PROJECT_ID" - policy: push - paths: - - build - - .gradle + +checkRun: + stage: test + script: ./gradlew bootRun -Dng-tracker.check-run=true + +checkStyle: + stage: test + script: ./gradlew check -x test deploy: stage: deploy @@ -33,12 +30,6 @@ deploy: - sh deploy/gdccloud/deploy.sh only: - dev - cache: - key: "$CI_PROJECT_ID" - policy: pull - paths: - - build - - .gradle environment: name: staging url: http://193.110.3.124:8080 diff --git a/.gitlab/issue_templates/feature.md b/.gitlab/issue_templates/feature.md new file mode 100644 index 0000000..c9e1757 --- /dev/null +++ b/.gitlab/issue_templates/feature.md @@ -0,0 +1,46 @@ +## Краткое описание задачи + +Что требуется сделать + + +## `Опционально` Список верстаемых страниц + +Будут затронуты страницы: +* page1.html +* page2.html +* page3.html + +## `Опционально` Список затрагиваемых модулей + +При реализации задачи потребуется также реализовать методы контроллера + + +## `Опционально` Список реализуемых функций + +После выполнения задачи станет доступным: +* просмотр `entity_name` +* редактирование `entity_name` +* валидация `entity_name` + +## `Опционально` Сценарии работы + +Сценарий просмотра: +1. Зайти на главную страницу приложения +2. Перейти в раздел `section_name` +3. Перейти к списку `entity_name` +4. Выбрать нужную `entity_name` и нажать на нее + +Сценарий редактирования: +1. Зайти на главную страницу приложения +2. Перейти в раздел `section_name` +3. Перейти к списку `entity_name` +4. Выбрать нужную `entity_name` и нажать на нее +5. Внести нужные правки в поля и сохранить + +## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы + +* Сверстаны страницы page1.hml, page2.hml, page3.hml +* Реализован контроллер для обслуживания страниц +* Сохранение в БД еще не реализовано +* Валидация происходит по полям `field1, field2` +* Сценарий просмотра проверяется при ручном внечении записей в БД diff --git a/build.gradle b/build.gradle index 53f06a9..0edeb92 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,10 @@ bootRun.dependsOn checkstyleMain sourceCompatibility = 1.8 targetCompatibility = 1.8 +bootRun { + systemProperties = System.properties +} + checkstyle { project.ext.checkstyleVersion = '8.8' @@ -110,6 +114,7 @@ dependencies { compile group: 'org.liquibase', name: 'liquibase-core', version: '3.6.3' compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0' + compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' compile group: 'org.webjars', name: 'bootstrap', version: '4.1.0' @@ -118,8 +123,10 @@ dependencies { compile group: 'org.webjars.npm', name: 'jquery.easing', version: '1.4.1' compile group: 'org.webjars', name: 'font-awesome', version: '4.7.0' - compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0' - compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0' + compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.6.0' + compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.6.0' testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test' + testCompile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.3.1' + } \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index 5877ec9..828cdaa 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -100,7 +100,7 @@ - + + diff --git a/src/main/java/ru/ulstu/NgTrackerApplication.java b/src/main/java/ru/ulstu/NgTrackerApplication.java index 5e6ee4f..fbe3a2f 100644 --- a/src/main/java/ru/ulstu/NgTrackerApplication.java +++ b/src/main/java/ru/ulstu/NgTrackerApplication.java @@ -2,13 +2,30 @@ package ru.ulstu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import ru.ulstu.configuration.ApplicationProperties; import ru.ulstu.core.repository.JpaDetachableRepositoryImpl; @SpringBootApplication @EnableJpaRepositories(repositoryBaseClass = JpaDetachableRepositoryImpl.class) public class NgTrackerApplication { + private final ApplicationProperties applicationProperties; + + public NgTrackerApplication(ApplicationProperties applicationProperties) { + this.applicationProperties = applicationProperties; + } + public static void main(String[] args) { SpringApplication.run(NgTrackerApplication.class, args); } + + @EventListener(ApplicationReadyEvent.class) + public void doSomethingAfterStartup() { + System.out.println("hello world, I have just started up"); + if (applicationProperties.isCheckRun()) { + System.exit(0); + } + } } diff --git a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java index aa871ba..a416345 100644 --- a/src/main/java/ru/ulstu/conference/controller/ConferenceController.java +++ b/src/main/java/ru/ulstu/conference/controller/ConferenceController.java @@ -2,10 +2,35 @@ package ru.ulstu.conference.controller; import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.Errors; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +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; +import javax.validation.Valid; +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; + + @Controller() @RequestMapping(value = "/conferences") @ApiIgnore @@ -13,7 +38,119 @@ public class ConferenceController { private final ConferenceService conferenceService; - public ConferenceController(ConferenceService paperService) { - this.conferenceService = paperService; + public ConferenceController(ConferenceService conferenceService) { + this.conferenceService = conferenceService; + } + + @GetMapping("/conferences") + public void getConferences(ModelMap modelMap) { + modelMap.put("filteredConferences", new ConferenceFilterDto(conferenceService.findAllDto())); + } + + @PostMapping("/conferences") + public void filterConferences(@Valid ConferenceFilterDto conferenceFilterDto, ModelMap modelMap) { + modelMap.put("filteredConferences", new ConferenceFilterDto(conferenceService.filter(conferenceFilterDto), + conferenceFilterDto.getFilterUserId(), + conferenceFilterDto.getYear())); + } + + @GetMapping("/dashboard") + public void getDashboard(ModelMap modelMap) { + modelMap.put("conferences", conferenceService.findAllActiveDto()); + } + + @GetMapping("/conference") + public void getConference(ModelMap modelMap, @RequestParam(value = "id") Integer id) { + if (id != null && id > 0) { + modelMap.put("conferenceDto", conferenceService.getExistConferenceById(id)); + } else { + modelMap.put("conferenceDto", conferenceService.getNewConference()); + } + } + + @PostMapping(value = "/conference", params = "save") + public String save(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException { + filterEmptyDeadlines(conferenceDto); + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceService.save(conferenceDto); + return String.format(REDIRECT_TO, CONFERENCES_PAGE); + + } + + @GetMapping("/delete/{conference-id}") + public String delete(@PathVariable("conference-id") Integer conferenceId) throws IOException { + conferenceService.delete(conferenceId); + return String.format(REDIRECT_TO, CONFERENCES_PAGE); + } + + @PostMapping(value = "/conference", params = "addDeadline") + public String addDeadline(@Valid ConferenceDto conferenceDto, Errors errors) { + filterEmptyDeadlines(conferenceDto); + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceDto.getDeadlines().add(new Deadline()); + return CONFERENCE_PAGE; + } + + @PostMapping(value = "/conference", params = "removeDeadline") + public String removeDeadline(@Valid ConferenceDto conferenceDto, Errors errors, + @RequestParam(value = "removeDeadline") Integer deadlineIndex) throws IOException { + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceService.removeDeadline(conferenceDto, deadlineIndex); + return CONFERENCE_PAGE; + } + + @PostMapping(value = "/conference", params = "removePaper") + public String removePaper(@Valid ConferenceDto conferenceDto, Errors errors, + @RequestParam(value = "removePaper") Integer paperIndex) throws IOException { + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceService.removePaper(conferenceDto, paperIndex); + return CONFERENCE_PAGE; + } + + @PostMapping(value = "/conference", params = "takePart") + public String takePart(@Valid ConferenceDto conferenceDto, Errors errors) throws IOException { + if (errors.hasErrors()) { + return CONFERENCE_PAGE; + } + conferenceService.takePart(conferenceDto); + return CONFERENCE_PAGE; + } + + @ModelAttribute("allParticipation") + public List getAllParticipation() { + return conferenceService.getAllParticipations(); + } + + @ModelAttribute("allDeposit") + public List getAllDeposit() { + return conferenceService.getAllDeposit(); + } + + @ModelAttribute("allUsers") + public List getAllUsers() { + return conferenceService.getAllUsers(); + } + + @ModelAttribute("allYears") + public List getAllYears() { + List years = new ArrayList<>(); + for (int i = Calendar.getInstance().get(Calendar.YEAR); i > 2010; i--) { + years.add(i); + } + 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 b2851ed..e252e4d 100644 --- a/src/main/java/ru/ulstu/conference/model/Conference.java +++ b/src/main/java/ru/ulstu/conference/model/Conference.java @@ -1,11 +1,138 @@ package ru.ulstu.conference.model; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.format.annotation.DateTimeFormat; import ru.ulstu.core.model.BaseEntity; +import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.paper.model.Paper; +import javax.persistence.CascadeType; +import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; @Entity @Table(name = "conference") public class Conference extends BaseEntity { + + @NotBlank + private String title; + + private String description; + + private String url; + + private int ping; + + @Column(name = "begin_date") + @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date beginDate; + + @Column(name = "end_date") + @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "conference_id", unique = true) + @Fetch(FetchMode.SUBSELECT) + @OrderBy("date") + private List deadlines = new ArrayList<>(); + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "paper_conference", + joinColumns = {@JoinColumn(name = "conference_id")}, + inverseJoinColumns = {@JoinColumn(name = "paper_id")}) + private List papers = new ArrayList<>(); + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "conference_id", unique = true) + @Fetch(FetchMode.SUBSELECT) + private List users = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getPing() { + return ping; + } + + public void setPing(int ping) { + this.ping = ping; + } + + public Date getBeginDate() { + return beginDate; + } + + public void setBeginDate(Date beginDate) { + this.beginDate = beginDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + public List getPapers() { + return papers; + } + + public void setPapers(List papers) { + this.papers = papers; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } } diff --git a/src/main/java/ru/ulstu/conference/model/ConferenceDto.java b/src/main/java/ru/ulstu/conference/model/ConferenceDto.java index e3f757e..2e2f13b 100644 --- a/src/main/java/ru/ulstu/conference/model/ConferenceDto.java +++ b/src/main/java/ru/ulstu/conference/model/ConferenceDto.java @@ -1,4 +1,221 @@ package ru.ulstu.conference.model; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.NotEmpty; +import org.springframework.format.annotation.DateTimeFormat; +import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.paper.model.Paper; + +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.validation.constraints.Size; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static ru.ulstu.core.util.StreamApiUtils.convert; + public class ConferenceDto { + + private final static String BEGIN_DATE = "Начало: "; + private final static String END_DATE = "Конец: "; + + private Integer id; + @NotEmpty + @Size(min = 2, max = 400) + private String title; + @Size(max = 500) + private String description = ""; + @Size(max = 255) + private String url = ""; + private int ping = 0; + @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date beginDate = new Date(); + @Temporal(TemporalType.TIMESTAMP) + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date endDate = new Date(); + private List deadlines = new ArrayList<>(); + private List removedDeadlineIds = new ArrayList<>(); + private List userIds = new ArrayList<>(); + private List paperIds = new ArrayList<>(); + private List papers = new ArrayList<>(); + private List notSelectedPapers = new ArrayList<>(); + private List users = new ArrayList<>(); + private boolean disabledTakePart = false; + + public ConferenceDto() { + } + + @JsonCreator + public ConferenceDto(@JsonProperty("id") Integer id, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("url") String url, + @JsonProperty("ping") Integer ping, + @JsonProperty("beginDate") Date beginDate, + @JsonProperty("endDate") Date endDate, + @JsonProperty("deadlines") List deadlines, + @JsonProperty("userIds") List userIds, + @JsonProperty("paperIds") List paperIds, + @JsonProperty("users") List users, + @JsonProperty("papers") List papers, + @JsonProperty("notSelectedPapers") List notSelectedPapers, + @JsonProperty("notSelectedPapers") Boolean disabledTakePart) { + this.id = id; + this.title = title; + this.description = description; + this.url = url; + this.ping = ping; + this.beginDate = beginDate; + this.endDate = endDate; + this.deadlines = deadlines; + this.userIds = userIds; + this.paperIds = paperIds; + this.users = users; + this.papers = papers; + this.notSelectedPapers = notSelectedPapers; + this.disabledTakePart = disabledTakePart; + } + + public ConferenceDto(Conference conference) { + this.id = conference.getId(); + this.title = conference.getTitle(); + this.description = conference.getDescription(); + this.url = conference.getUrl(); + this.ping = conference.getPing(); + this.beginDate = conference.getBeginDate(); + this.endDate = conference.getEndDate(); + this.deadlines = conference.getDeadlines(); + this.userIds = convert(conference.getUsers(), user -> user.getId()); + this.paperIds = convert(conference.getPapers(), paper -> paper.getId()); + this.users = conference.getUsers(); + this.papers = conference.getPapers(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getPing() { + return ping; + } + + public void setPing(int ping) { + this.ping = ping; + } + + public Date getBeginDate() { + return beginDate; + } + + public void setBeginDate(Date beginDate) { + this.beginDate = beginDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + public List getUserIds() { + return userIds; + } + + public void setUserIds(List userIds) { + this.userIds = userIds; + } + + public List getPaperIds() { + return paperIds; + } + + public void setPaperIds(List paperIds) { + this.paperIds = paperIds; + } + + public List getPapers() { + return papers; + } + + public void setPapers(List papers) { + this.papers = papers; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public boolean isDisabledTakePart() { + return disabledTakePart; + } + + public void setDisabledTakePart(boolean disabledTakePart) { + this.disabledTakePart = disabledTakePart; + } + + public List getRemovedDeadlineIds() { + return removedDeadlineIds; + } + + public void setRemovedDeadlineIds(List removedDeadlineIds) { + this.removedDeadlineIds = removedDeadlineIds; + } + + public List getNotSelectedPapers() { + return notSelectedPapers; + } + + public void setNotSelectedPapers(List notSelectedPapers) { + this.notSelectedPapers = notSelectedPapers; + } + + public String getDatesString() { + return BEGIN_DATE + beginDate.toString().split(" ")[0] + " " + END_DATE + endDate.toString().split(" ")[0]; + } + } diff --git a/src/main/java/ru/ulstu/conference/model/ConferenceFilterDto.java b/src/main/java/ru/ulstu/conference/model/ConferenceFilterDto.java index 379c8b6..8503b40 100644 --- a/src/main/java/ru/ulstu/conference/model/ConferenceFilterDto.java +++ b/src/main/java/ru/ulstu/conference/model/ConferenceFilterDto.java @@ -1,4 +1,47 @@ package ru.ulstu.conference.model; +import java.util.List; + public class ConferenceFilterDto { + + private List conferences; + private Integer filterUserId; + private Integer year; + + public ConferenceFilterDto() { + } + + public ConferenceFilterDto(List conferenceDtos, Integer filterUserId, Integer year) { + this.conferences = conferenceDtos; + this.filterUserId = filterUserId; + this.year = year; + } + + public ConferenceFilterDto(List conferenceDtos) { + this(conferenceDtos, null, null); + } + + public List getConferences() { + return conferences; + } + + public void setConferences(List conferences) { + this.conferences = conferences; + } + + public Integer getFilterUserId() { + return filterUserId; + } + + public void setFilterUserId(Integer filterUserId) { + this.filterUserId = filterUserId; + } + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } } diff --git a/src/main/java/ru/ulstu/conference/model/ConferenceUser.java b/src/main/java/ru/ulstu/conference/model/ConferenceUser.java new file mode 100644 index 0000000..63eeb6d --- /dev/null +++ b/src/main/java/ru/ulstu/conference/model/ConferenceUser.java @@ -0,0 +1,110 @@ +package ru.ulstu.conference.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import ru.ulstu.core.model.BaseEntity; +import ru.ulstu.user.model.User; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +@Entity +@Table(name = "users_conference") +public class ConferenceUser extends BaseEntity { + + public enum Participation { + INTRAMURAL("Очная"), + EXTRAMURAL("Заочная"); + + private String participationName; + + Participation(String name) { + this.participationName = name; + } + + public String getParticipation() { + return participationName; + } + } + + public enum Deposit { + ARTICLE("Статья"), + REPORT("Доклад"), + PRESENTATION("Презентация"); + + private String depositName; + + Deposit(String name) { + this.depositName = name; + } + + public String getDeposit() { + return depositName; + } + } + + + @NotNull + @Column(name = "participation", nullable = false) + @Enumerated(value = EnumType.STRING) + private Participation participation = Participation.INTRAMURAL; + + @NotNull + @Column(name = "deposit", nullable = false) + @Enumerated(value = EnumType.STRING) + private Deposit deposit = Deposit.ARTICLE; + + @ManyToOne(optional = false) + @JoinColumn(name = "users_id") + private User user; + + public ConferenceUser() { + } + + public ConferenceUser(User user) { + this.user = user; + } + + @JsonCreator + public ConferenceUser(@JsonProperty("id") Integer id, + @JsonProperty("deposit") Participation participation, + @JsonProperty("deposit") Deposit deposit, + @JsonProperty("user") User user) { + this.setId(id); + this.participation = participation; + this.deposit = deposit; + this.user = user; + } + + + public Participation getParticipation() { + return participation; + } + + public void setParticipation(Participation participation) { + this.participation = participation; + } + + public Deposit getDeposit() { + return deposit; + } + + public void setDeposit(Deposit deposit) { + this.deposit = deposit; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + +} diff --git a/src/main/java/ru/ulstu/conference/repository/ConferenceRepository.java b/src/main/java/ru/ulstu/conference/repository/ConferenceRepository.java index 4325633..cb8a488 100644 --- a/src/main/java/ru/ulstu/conference/repository/ConferenceRepository.java +++ b/src/main/java/ru/ulstu/conference/repository/ConferenceRepository.java @@ -1,7 +1,19 @@ package ru.ulstu.conference.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.user.model.User; + +import java.util.Date; +import java.util.List; public interface ConferenceRepository extends JpaRepository { + @Query("SELECT c FROM Conference c LEFT JOIN c.users u WHERE (:user IS NULL OR u.user = :user) " + + "AND (YEAR(c.beginDate) = :year OR :year IS NULL) ORDER BY begin_date DESC") + List findByUserAndYear(@Param("user") User user, @Param("year") Integer year); + + @Query("SELECT c FROM Conference c WHERE c.beginDate > :date") + List findAllActive(@Param("date") Date date); } diff --git a/src/main/java/ru/ulstu/conference/repository/ConferenceUserRepository.java b/src/main/java/ru/ulstu/conference/repository/ConferenceUserRepository.java new file mode 100644 index 0000000..5e654d5 --- /dev/null +++ b/src/main/java/ru/ulstu/conference/repository/ConferenceUserRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.conference.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.conference.model.ConferenceUser; + +public interface ConferenceUserRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceService.java b/src/main/java/ru/ulstu/conference/service/ConferenceService.java index a8147c9..dfd0f8a 100644 --- a/src/main/java/ru/ulstu/conference/service/ConferenceService.java +++ b/src/main/java/ru/ulstu/conference/service/ConferenceService.java @@ -1,17 +1,176 @@ package ru.ulstu.conference.service; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +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.service.DeadlineService; +import ru.ulstu.paper.model.Paper; +import ru.ulstu.paper.service.PaperService; +import ru.ulstu.user.model.User; +import ru.ulstu.user.service.UserService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import static org.springframework.util.ObjectUtils.isEmpty; +import static ru.ulstu.core.util.StreamApiUtils.convert; @Service public class ConferenceService { + private final static int MAX_DISPLAY_SIZE = 40; + private final ConferenceRepository conferenceRepository; + private final ConferenceUserService conferenceUserService; private final DeadlineService deadlineService; + private final PaperService paperService; + private final UserService userService; public ConferenceService(ConferenceRepository conferenceRepository, - DeadlineService deadlineService) { + ConferenceUserService conferenceUserService, + DeadlineService deadlineService, + PaperService paperService, + UserService userService) { this.conferenceRepository = conferenceRepository; + this.conferenceUserService = conferenceUserService; this.deadlineService = deadlineService; + this.paperService = paperService; + this.userService = userService; + } + + public ConferenceDto getExistConferenceById(Integer id) { + ConferenceDto conferenceDto = findOneDto(id); + conferenceDto.setNotSelectedPapers(getNotSelectPapers(conferenceDto.getPaperIds())); + conferenceDto.setDisabledTakePart(isCurrentUserParticipant(conferenceDto.getUsers())); + return conferenceDto; + } + + public ConferenceDto getNewConference() { + ConferenceDto conferenceDto = new ConferenceDto(); + conferenceDto.setNotSelectedPapers(getNotSelectPapers(new ArrayList())); + return conferenceDto; + } + + + public List findAll() { + return conferenceRepository.findAll(new Sort(Sort.Direction.DESC, "beginDate")); + } + + public List findAllDto() { + List conferences = convert(findAll(), ConferenceDto::new); + conferences.forEach(conferenceDto -> conferenceDto.setTitle(StringUtils.abbreviate(conferenceDto.getTitle(), MAX_DISPLAY_SIZE))); + return conferences; + } + + public ConferenceDto findOneDto(Integer id) { + return new ConferenceDto(conferenceRepository.getOne(id)); + } + + public void save(ConferenceDto conferenceDto) throws IOException { + if (isEmpty(conferenceDto.getId())) { + create(conferenceDto); + } else { + update(conferenceDto); + } + } + + @Transactional + public Integer create(ConferenceDto conferenceDto) throws IOException { + Conference newConference = copyFromDto(new Conference(), conferenceDto); + newConference = conferenceRepository.save(newConference); + return newConference.getId(); + } + + @Transactional + public Integer update(ConferenceDto conferenceDto) throws IOException { + Conference conference = conferenceRepository.getOne(conferenceDto.getId()); + conferenceRepository.save(copyFromDto(conference, conferenceDto)); + conferenceDto.getRemovedDeadlineIds().forEach(deadlineService::remove); + return conference.getId(); + } + + @Transactional + public void delete(Integer conferenceId) { + if (conferenceRepository.existsById(conferenceId)) { + conferenceRepository.deleteById(conferenceId); + } + } + + public void removeDeadline(ConferenceDto conferenceDto, Integer deadlineIndex) throws IOException { + if (conferenceDto.getDeadlines().get(deadlineIndex).getId() != null) { + conferenceDto.getRemovedDeadlineIds().add(conferenceDto.getDeadlines().get(deadlineIndex).getId()); + } + conferenceDto.getDeadlines().remove((int) deadlineIndex); + } + + public void removePaper(ConferenceDto conferenceDto, Integer paperIndex) throws IOException { + Paper removedPaper = conferenceDto.getPapers().remove((int) paperIndex); + conferenceDto.getNotSelectedPapers().add(removedPaper); + } + + public void takePart(ConferenceDto conferenceDto) throws IOException { + conferenceDto.getUsers().add(new ConferenceUser(userService.getCurrentUser())); + conferenceDto.setDisabledTakePart(true); + } + + public List getNotSelectPapers(List paperIds) { + return paperService.findAllNotSelect(paperIds); + } + + public List getAllUsers() { + return userService.findAll(); + } + + public List getAllParticipations() { + return Arrays.asList(ConferenceUser.Participation.values()); + } + + public List getAllDeposit() { + return Arrays.asList(ConferenceUser.Deposit.values()); + } + + private Conference copyFromDto(Conference conference, ConferenceDto conferenceDto) throws IOException { + conference.setTitle(conferenceDto.getTitle()); + conference.setDescription(conferenceDto.getDescription()); + conference.setUrl(conferenceDto.getUrl()); + conference.setPing(0); + conference.setBeginDate(conferenceDto.getBeginDate()); + conference.setEndDate(conferenceDto.getEndDate()); + conference.setPapers(conferenceDto.getPapers()); + conference.setDeadlines(deadlineService.saveOrCreate(conferenceDto.getDeadlines())); + conference.setUsers(conferenceUserService.saveOrCreate(conferenceDto.getUsers())); + if (conferenceDto.getPaperIds() != null && !conferenceDto.getPaperIds().isEmpty()) { + conferenceDto.getPaperIds().forEach(paperId -> + conference.getPapers().add(paperService.findEntityById(paperId))); + } + return conference; + } + + + public boolean isCurrentUserParticipant(List conferenceUsers) { + return conferenceUsers.stream().anyMatch(participant -> participant.getUser().equals(userService.getCurrentUser())); + } + + public List filter(ConferenceFilterDto conferenceFilterDto) { + return convert(conferenceRepository.findByUserAndYear( + conferenceFilterDto.getFilterUserId() == null ? null : userService.findById(conferenceFilterDto.getFilterUserId()), + conferenceFilterDto.getYear()), ConferenceDto::new); + + } + + public List findAllActiveDto() { + return convert(findAllActive(), ConferenceDto::new); + } + + public List findAllActive() { + return conferenceRepository.findAllActive(new Date()); } } diff --git a/src/main/java/ru/ulstu/conference/service/ConferenceUserService.java b/src/main/java/ru/ulstu/conference/service/ConferenceUserService.java new file mode 100644 index 0000000..269e7df --- /dev/null +++ b/src/main/java/ru/ulstu/conference/service/ConferenceUserService.java @@ -0,0 +1,48 @@ +package ru.ulstu.conference.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.conference.model.ConferenceUser; +import ru.ulstu.conference.repository.ConferenceUserRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class ConferenceUserService { + + private final ConferenceUserRepository conferenceUserRepository; + + public ConferenceUserService(ConferenceUserRepository conferenceUserRepository) { + this.conferenceUserRepository = conferenceUserRepository; + } + + public List saveOrCreate(List users) { + return users + .stream() + .map(user -> { + return user.getId() != null ? update(user) : create(user); + }).collect(Collectors.toList()); + } + + @Transactional + public ConferenceUser update(ConferenceUser user) { + ConferenceUser updateUser = conferenceUserRepository.getOne(user.getId()); + updateUser.setDeposit(user.getDeposit()); + updateUser.setParticipation(user.getParticipation()); + conferenceUserRepository.save(updateUser); + return updateUser; + } + + @Transactional + public ConferenceUser create(ConferenceUser user) { + ConferenceUser newUser = new ConferenceUser(); + newUser.setDeposit(user.getDeposit()); + newUser.setParticipation(user.getParticipation()); + newUser.setUser(user.getUser()); + newUser = conferenceUserRepository.save(newUser); + return newUser; + } + + +} diff --git a/src/main/java/ru/ulstu/configuration/ApplicationProperties.java b/src/main/java/ru/ulstu/configuration/ApplicationProperties.java index 339bcb7..0b09821 100644 --- a/src/main/java/ru/ulstu/configuration/ApplicationProperties.java +++ b/src/main/java/ru/ulstu/configuration/ApplicationProperties.java @@ -12,12 +12,16 @@ import javax.validation.constraints.NotBlank; public class ApplicationProperties { @NotBlank private String baseUrl; + @NotBlank private String undeadUserLogin; + private boolean devMode; private boolean useHttps; + private boolean checkRun; + public boolean isUseHttps() { return useHttps; } @@ -49,4 +53,12 @@ public class ApplicationProperties { public void setDevMode(boolean devMode) { this.devMode = devMode; } + + public boolean isCheckRun() { + return checkRun; + } + + public void setCheckRun(boolean checkRun) { + this.checkRun = checkRun; + } } diff --git a/src/main/java/ru/ulstu/core/controller/AdviceController.java b/src/main/java/ru/ulstu/core/controller/AdviceController.java index 1c71df1..8238797 100644 --- a/src/main/java/ru/ulstu/core/controller/AdviceController.java +++ b/src/main/java/ru/ulstu/core/controller/AdviceController.java @@ -20,7 +20,6 @@ import ru.ulstu.user.error.UserNotActivatedException; import ru.ulstu.user.error.UserNotFoundException; import ru.ulstu.user.error.UserPasswordsNotValidOrNotMatchException; import ru.ulstu.user.error.UserResetKeyError; -import ru.ulstu.user.model.User; import ru.ulstu.user.service.UserService; import java.util.Set; @@ -28,7 +27,6 @@ import java.util.stream.Collectors; @ControllerAdvice public class AdviceController { - private final static String USER_NAME_TEMPLATE = "%s %s %s"; private final Logger log = LoggerFactory.getLogger(AdviceController.class); private final UserService userService; @@ -38,11 +36,7 @@ public class AdviceController { @ModelAttribute("currentUser") public String getCurrentUser() { - User user = userService.getCurrentUser(); - return String.format(USER_NAME_TEMPLATE, - user.getLastName(), - user.getFirstName().substring(0, 1), - user.getPatronymic().substring(0, 1)); + return userService.getCurrentUser().getUserAbbreviate(); } private Response handleException(ErrorConstants error) { diff --git a/src/main/java/ru/ulstu/grant/controller/Navigation.java b/src/main/java/ru/ulstu/core/controller/Navigation.java similarity index 69% rename from src/main/java/ru/ulstu/grant/controller/Navigation.java rename to src/main/java/ru/ulstu/core/controller/Navigation.java index bf1626f..caea429 100644 --- a/src/main/java/ru/ulstu/grant/controller/Navigation.java +++ b/src/main/java/ru/ulstu/core/controller/Navigation.java @@ -1,4 +1,4 @@ -package ru.ulstu.grant.controller; +package ru.ulstu.core.controller; import org.springframework.validation.Errors; @@ -7,6 +7,9 @@ public class Navigation { public static final String GRANTS_PAGE = "/grants/grants"; public static final String GRANT_PAGE = "/grants/grant"; + public static final String CONFERENCES_PAGE = "/conferences/conferences"; + public static final String CONFERENCE_PAGE = "/conferences/conference"; + public static String hasErrors(Errors errors, String page) { if (errors.hasErrors()) { return page; diff --git a/src/main/java/ru/ulstu/core/error/XlsLoadException.java b/src/main/java/ru/ulstu/core/error/XlsLoadException.java deleted file mode 100644 index 372731f..0000000 --- a/src/main/java/ru/ulstu/core/error/XlsLoadException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.ulstu.core.error; - -public class XlsLoadException extends Exception { - public XlsLoadException(String s) { - super(s); - } -} diff --git a/src/main/java/ru/ulstu/core/error/XlsParseException.java b/src/main/java/ru/ulstu/core/error/XlsParseException.java deleted file mode 100644 index fe8c4b3..0000000 --- a/src/main/java/ru/ulstu/core/error/XlsParseException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.ulstu.core.error; - -public class XlsParseException extends Exception { - public XlsParseException(String s) { - super(s); - } -} diff --git a/src/main/java/ru/ulstu/core/model/BaseEntity.java b/src/main/java/ru/ulstu/core/model/BaseEntity.java index bd0f1e4..d3dfa76 100644 --- a/src/main/java/ru/ulstu/core/model/BaseEntity.java +++ b/src/main/java/ru/ulstu/core/model/BaseEntity.java @@ -1,6 +1,10 @@ package ru.ulstu.core.model; -import javax.persistence.*; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Version; import java.io.Serializable; @MappedSuperclass diff --git a/src/main/java/ru/ulstu/core/service/TreeService.java b/src/main/java/ru/ulstu/core/service/TreeService.java deleted file mode 100644 index 1e3d4a0..0000000 --- a/src/main/java/ru/ulstu/core/service/TreeService.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.ulstu.core.service; - -import org.springframework.stereotype.Service; -import ru.ulstu.core.model.TreeDto; -import ru.ulstu.core.model.TreeEntity; - -import java.util.List; -import java.util.function.Predicate; - -@Service -public class TreeService { - public TreeDto getTree(String rootName, List rootItems) { - return addChildNode(new TreeDto(rootName), rootItems, element -> true); - } - - public TreeDto getTree(String rootName, List rootItems, Predicate filterPredicate) { - return addChildNode(new TreeDto(rootName), rootItems, filterPredicate); - } - - private TreeDto addChildNode(TreeDto currentRoot, List children, Predicate filterPredicate) { - if (children != null) { - children.stream() - .filter(filterPredicate) - .forEach(item -> { - TreeDto newNode = new TreeDto(item); - currentRoot.getChildren().add(addChildNode(newNode, item.getChildren(), filterPredicate)); - }); - } - return currentRoot; - } -} diff --git a/src/main/java/ru/ulstu/core/util/DateUtils.java b/src/main/java/ru/ulstu/core/util/DateUtils.java index b24b09c..6122583 100644 --- a/src/main/java/ru/ulstu/core/util/DateUtils.java +++ b/src/main/java/ru/ulstu/core/util/DateUtils.java @@ -1,6 +1,11 @@ package ru.ulstu.core.util; -import java.time.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -24,7 +29,7 @@ public class DateUtils { return cal; } - public static List getMonths () { + public static List getMonths() { return Arrays.asList(Month.values()); } diff --git a/src/main/java/ru/ulstu/deadline/service/DeadlineService.java b/src/main/java/ru/ulstu/deadline/service/DeadlineService.java index 30aa6b0..cb80bfe 100644 --- a/src/main/java/ru/ulstu/deadline/service/DeadlineService.java +++ b/src/main/java/ru/ulstu/deadline/service/DeadlineService.java @@ -41,4 +41,9 @@ public class DeadlineService { newDeadline = deadlineRepository.save(newDeadline); return newDeadline; } + + @Transactional + public void remove(Integer deadlineId) { + deadlineRepository.deleteById(deadlineId); + } } diff --git a/src/main/java/ru/ulstu/file/model/FileData.java b/src/main/java/ru/ulstu/file/model/FileData.java index e5b3277..3f97130 100644 --- a/src/main/java/ru/ulstu/file/model/FileData.java +++ b/src/main/java/ru/ulstu/file/model/FileData.java @@ -19,6 +19,9 @@ public class FileData extends BaseEntity { private byte[] data; + @Column(name = "is_latex_attach") + private Boolean isLatexAttach; + public String getName() { return name; } @@ -50,4 +53,12 @@ public class FileData extends BaseEntity { public void setData(byte[] data) { this.data = data; } + + public Boolean isLatexAttach() { + return isLatexAttach; + } + + public void setLatexAttach(Boolean latexAttach) { + isLatexAttach = latexAttach; + } } diff --git a/src/main/java/ru/ulstu/file/model/FileDataDto.java b/src/main/java/ru/ulstu/file/model/FileDataDto.java index 919cd80..e83bf12 100644 --- a/src/main/java/ru/ulstu/file/model/FileDataDto.java +++ b/src/main/java/ru/ulstu/file/model/FileDataDto.java @@ -9,6 +9,7 @@ public class FileDataDto { private String fileName; private String tmpFileName; private boolean deleted; + private Boolean isLatexAttach; public FileDataDto() { } @@ -16,17 +17,20 @@ public class FileDataDto { @JsonCreator public FileDataDto(@JsonProperty("id") Integer id, @JsonProperty("name") String name, + @JsonProperty("isLatexAttach") Boolean isLatexAttach, @JsonProperty("fileName") String fileName, @JsonProperty("tmpFileName") String tmpFileName) { this.id = id; this.name = name; this.fileName = fileName; this.tmpFileName = tmpFileName; + this.isLatexAttach = isLatexAttach; } public FileDataDto(FileData fileData) { this.id = fileData.getId(); this.name = fileData.getName(); + this.isLatexAttach = fileData.isLatexAttach(); } public FileDataDto(String fileName, String tmpFileName) { @@ -73,4 +77,19 @@ public class FileDataDto { this.deleted = deleted; } + public Boolean isLatexAttach() { + return isLatexAttach; + } + + public Boolean getIsLatexAttach() { + return isLatexAttach; + } + + public void setLatexAttach(Boolean latexAttach) { + isLatexAttach = latexAttach; + } + + public void setIsLatexAttach(Boolean latexAttach) { + isLatexAttach = latexAttach; + } } diff --git a/src/main/java/ru/ulstu/file/service/FileService.java b/src/main/java/ru/ulstu/file/service/FileService.java index 85dd91c..a8e705a 100644 --- a/src/main/java/ru/ulstu/file/service/FileService.java +++ b/src/main/java/ru/ulstu/file/service/FileService.java @@ -6,7 +6,10 @@ import org.springframework.web.multipart.MultipartFile; import ru.ulstu.file.model.FileData; import ru.ulstu.file.model.FileDataDto; import ru.ulstu.file.repostory.FileRepository; +import ru.ulstu.paper.model.PaperDto; +import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -15,6 +18,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; @@ -94,7 +98,7 @@ public class FileService { @Transactional public FileData update(FileDataDto fileDataDto) { - FileData file = fileRepository.findOne(fileDataDto.getId()); + FileData file = fileRepository.getOne(fileDataDto.getId()); return fileRepository.save(copyFromDto(file, fileDataDto)); } @@ -107,15 +111,39 @@ public class FileService { private FileData copyFromDto(FileData fileData, FileDataDto fileDataDto) { fileData.setName(fileDataDto.getName()); + fileData.setLatexAttach(fileDataDto.isLatexAttach()); return fileData; } @Transactional public void delete(Integer fileId) { - fileRepository.delete(fileRepository.findOne(fileId)); + fileRepository.delete(fileRepository.getOne(fileId)); } public FileDataDto createFromMultipartFile(MultipartFile multipartFile) throws IOException { return new FileDataDto(multipartFile.getOriginalFilename(), uploadToTmpDir(multipartFile)); } + + public void createLatexAttachs(PaperDto paper) throws IOException { + for (FileDataDto fileDataDto : paper.getFiles() + .stream() + .filter(f -> (f.isLatexAttach() != null && f.isLatexAttach()) && !f.isDeleted()) + .collect(Collectors.toList())) { + if (fileDataDto.getId() == null) { + File oldFile = getTmpFilePath(fileDataDto.getTmpFileName()).toFile(); + File renamed = getTmpFilePath(fileDataDto.getName()).toFile(); + oldFile.renameTo(renamed); + } else { + Files.write(getTmpFilePath(fileDataDto.getName()), fileRepository.getOne(fileDataDto.getId()).getData()); + } + } + } + + public File createLatexFile(PaperDto paper) throws IOException { + BufferedWriter writer = Files.newBufferedWriter(getTmpFilePath(paper.getTitle() + ".tex")); + writer.write(paper.getLatexText()); + writer.close(); + + return getTmpFilePath(paper.getTitle() + ".tex").toFile(); + } } diff --git a/src/main/java/ru/ulstu/grant/controller/GrantController.java b/src/main/java/ru/ulstu/grant/controller/GrantController.java index 65b11dc..0731188 100644 --- a/src/main/java/ru/ulstu/grant/controller/GrantController.java +++ b/src/main/java/ru/ulstu/grant/controller/GrantController.java @@ -13,6 +13,7 @@ import ru.ulstu.deadline.model.Deadline; import ru.ulstu.grant.model.Grant; import ru.ulstu.grant.model.GrantDto; import ru.ulstu.grant.service.GrantService; +import ru.ulstu.user.model.User; import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; @@ -21,10 +22,9 @@ import java.util.List; import java.util.stream.Collectors; import static org.springframework.util.StringUtils.isEmpty; -import static ru.ulstu.grant.controller.Navigation.GRANTS_PAGE; -import static ru.ulstu.grant.controller.Navigation.GRANT_PAGE; -import static ru.ulstu.grant.controller.Navigation.REDIRECT_TO; -import static ru.ulstu.grant.controller.Navigation.hasErrors; +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.REDIRECT_TO; @Controller() @@ -57,27 +57,42 @@ public class GrantController { } @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); 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); return String.format(REDIRECT_TO, GRANTS_PAGE); } + @PostMapping(value = "/grant", params = "filterUsers") + public String filterUsers() { + return GRANT_PAGE; + } + @PostMapping(value = "/grant", params = "addDeadline") public String addDeadline(@Valid GrantDto grantDto, Errors errors) { filterEmptyDeadlines(grantDto); - hasErrors(errors, GRANT_PAGE); + if (errors.hasErrors()) { + return GRANT_PAGE; + } grantDto.getDeadlines().add(new Deadline()); return GRANT_PAGE; } @PostMapping(value = "/grant", params = "createProject") public String createProject(@Valid GrantDto grantDto, Errors errors) { - hasErrors(errors, GRANT_PAGE); + if (errors.hasErrors()) { + return GRANT_PAGE; + } grantService.createProject(grantDto); return GRANT_PAGE; } @@ -93,6 +108,11 @@ public class GrantController { return grantService.getGrantStatuses(); } + @ModelAttribute("allAuthors") + public List getAllAuthors(GrantDto grantDto) { + return grantService.getGrantAuthors(grantDto); + } + private void filterEmptyDeadlines(GrantDto grantDto) { grantDto.setDeadlines(grantDto.getDeadlines().stream() .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) diff --git a/src/main/java/ru/ulstu/grant/model/Grant.java b/src/main/java/ru/ulstu/grant/model/Grant.java index 09c2cdf..30d9fb3 100644 --- a/src/main/java/ru/ulstu/grant/model/Grant.java +++ b/src/main/java/ru/ulstu/grant/model/Grant.java @@ -1,29 +1,36 @@ package ru.ulstu.grant.model; 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.project.model.Project; +import ru.ulstu.user.model.User; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; +import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.OrderBy; import javax.persistence.Table; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; @Entity @Table(name = "grants") -public class Grant extends BaseEntity { +public class Grant extends BaseEntity implements UserContainer { public enum GrantStatus { APPLICATION("Заявка"), ON_COMPETITION("Отправлен на конкурс"), @@ -52,6 +59,7 @@ public class Grant extends BaseEntity { @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "grant_id") + @OrderBy("date") private List deadlines = new ArrayList<>(); //Описание гранта @@ -67,6 +75,14 @@ public class Grant extends BaseEntity { @JoinColumn(name = "project_id") private Project project; + @ManyToMany(fetch = FetchType.EAGER) + private Set authors = new HashSet<>(); + + @NotNull + @ManyToOne + @JoinColumn(name = "leader_id") + private User leader; + public GrantStatus getStatus() { return status; } @@ -115,6 +131,27 @@ public class Grant extends BaseEntity { this.project = project; } + public Set getAuthors() { + return authors; + } + + public void setAuthors(Set authors) { + this.authors = authors; + } + + @Override + public Set getUsers() { + return getAuthors(); + } + + public User getLeader() { + return leader; + } + + public void setLeader(User leader) { + this.leader = leader; + } + 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 1d0a43c..d836dfb 100644 --- a/src/main/java/ru/ulstu/grant/model/GrantDto.java +++ b/src/main/java/ru/ulstu/grant/model/GrantDto.java @@ -2,14 +2,22 @@ package ru.ulstu.grant.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.commons.lang3.StringUtils; import ru.ulstu.deadline.model.Deadline; import ru.ulstu.project.model.ProjectDto; +import ru.ulstu.user.model.UserDto; import javax.validation.constraints.NotEmpty; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static ru.ulstu.core.util.StreamApiUtils.convert; public class GrantDto { + private final static int MAX_AUTHORS_LENGTH = 60; + private Integer id; @NotEmpty private String title; @@ -18,6 +26,12 @@ public class GrantDto { private String comment; private String applicationFileName; private ProjectDto project; + private Set authorIds; + private Set authors; + private Integer leaderId; + private boolean wasLeader; + private boolean hasAge; + private boolean hasDegree; public GrantDto() { deadlines.add(new Deadline()); @@ -29,7 +43,13 @@ public class GrantDto { @JsonProperty("status") Grant.GrantStatus status, @JsonProperty("deadlines") List deadlines, @JsonProperty("comment") String comment, - @JsonProperty("project") ProjectDto project) { + @JsonProperty("project") ProjectDto project, + @JsonProperty("authorIds") Set authorIds, + @JsonProperty("authors") Set authors, + @JsonProperty("leader") Integer leaderId, + @JsonProperty("wasLeader") boolean wasLeader, + @JsonProperty("hasAge") boolean hasAge, + @JsonProperty("hasDegree") boolean hasDegree) { this.id = id; this.title = title; this.status = status; @@ -37,6 +57,11 @@ public class GrantDto { this.comment = comment; this.applicationFileName = null; this.project = project; + this.authors = authors; + this.leaderId = leaderId; + this.wasLeader = wasLeader; + this.hasAge = hasAge; + this.hasDegree = hasDegree; } public GrantDto(Grant grant) { @@ -47,6 +72,12 @@ public class GrantDto { this.comment = grant.getComment(); 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(); + this.wasLeader = false; + this.hasAge = false; + this.hasDegree = false; } public Integer getId() { @@ -104,4 +135,59 @@ public class GrantDto { public void setApplicationFileName(String applicationFileName) { this.applicationFileName = applicationFileName; } + + public Set getAuthorIds() { + return authorIds; + } + + public void setAuthorIds(Set authorIds) { + this.authorIds = authorIds; + } + + public Set getAuthors() { + return authors; + } + + public void setAuthors(Set 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; + } } diff --git a/src/main/java/ru/ulstu/grant/model/GrantStatusDto.java b/src/main/java/ru/ulstu/grant/model/GrantStatusDto.java deleted file mode 100644 index 34676d6..0000000 --- a/src/main/java/ru/ulstu/grant/model/GrantStatusDto.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/ru/ulstu/grant/repository/GrantRepository.java b/src/main/java/ru/ulstu/grant/repository/GrantRepository.java index 92dec43..44c2cc0 100644 --- a/src/main/java/ru/ulstu/grant/repository/GrantRepository.java +++ b/src/main/java/ru/ulstu/grant/repository/GrantRepository.java @@ -3,6 +3,9 @@ package ru.ulstu.grant.repository; import org.springframework.data.jpa.repository.JpaRepository; import ru.ulstu.grant.model.Grant; +import java.util.List; + public interface GrantRepository extends JpaRepository { + List findByStatus(Grant.GrantStatus status); } diff --git a/src/main/java/ru/ulstu/grant/service/GrantService.java b/src/main/java/ru/ulstu/grant/service/GrantService.java index 7041f7d..2aaef5d 100644 --- a/src/main/java/ru/ulstu/grant/service/GrantService.java +++ b/src/main/java/ru/ulstu/grant/service/GrantService.java @@ -12,11 +12,14 @@ import ru.ulstu.grant.repository.GrantRepository; import ru.ulstu.project.model.Project; import ru.ulstu.project.model.ProjectDto; import ru.ulstu.project.service.ProjectService; +import ru.ulstu.user.model.User; +import ru.ulstu.user.service.UserService; import java.io.IOException; 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; @@ -30,15 +33,18 @@ public class GrantService { private final ProjectService projectService; private final DeadlineService deadlineService; private final FileService fileService; + private final UserService userService; public GrantService(GrantRepository grantRepository, FileService fileService, DeadlineService deadlineService, - ProjectService projectService) { + ProjectService projectService, + UserService userService) { this.grantRepository = grantRepository; - this.projectService = projectService; this.fileService = fileService; this.deadlineService = deadlineService; + this.projectService = projectService; + this.userService = userService; } public List findAll() { @@ -73,6 +79,13 @@ public class GrantService { if (grantDto.getApplicationFileName() != null) { 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; } @@ -98,7 +111,6 @@ public class GrantService { if (grant.getApplication() != null) { fileService.deleteFile(grant.getApplication()); } - //возможно при удалении гранта будет удаляться и проект, к нему привязанный grantRepository.delete(grant); } @@ -107,13 +119,15 @@ public class GrantService { } @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.setTitle(title); grant.setComment("Комментарий к гранту 1"); grant.setProject(projectId); grant.setStatus(APPLICATION); grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн")); + grant.getAuthors().add(user); + grant.setLeader(user); grant = grantRepository.save(grant); return grant; } @@ -125,4 +139,22 @@ public class GrantService { update(grantDto); } } + + 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()); + } + return filteredUsers; + } + + private List getCompletedGrantLeaders() { + return grantRepository.findByStatus(Grant.GrantStatus.COMPLETED) + .stream() + .map(Grant::getLeader) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/ru/ulstu/odin/model/OdinField.java b/src/main/java/ru/ulstu/odin/model/OdinField.java index 17ee2dc..6beb7d9 100644 --- a/src/main/java/ru/ulstu/odin/model/OdinField.java +++ b/src/main/java/ru/ulstu/odin/model/OdinField.java @@ -31,14 +31,13 @@ public abstract class OdinField implements Comparable { return this.name().toLowerCase(); } } - - private Field field; protected final OdinFieldType fieldType; protected final String fieldName; protected final String caption; protected final OdinVisible.OdinVisibleType visible; protected final boolean readOnly; protected final boolean notEmpty; + private Field field; public OdinField(Field field, OdinFieldType fieldType) { this.field = field; @@ -126,8 +125,12 @@ public abstract class OdinField implements Comparable { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } OdinField odinField = (OdinField) o; return Objects.equals(fieldName, odinField.fieldName); } diff --git a/src/main/java/ru/ulstu/paper/controller/PaperController.java b/src/main/java/ru/ulstu/paper/controller/PaperController.java index 9169bd9..e11e4d9 100644 --- a/src/main/java/ru/ulstu/paper/controller/PaperController.java +++ b/src/main/java/ru/ulstu/paper/controller/PaperController.java @@ -1,5 +1,8 @@ package ru.ulstu.paper.controller; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.Errors; @@ -13,17 +16,20 @@ import ru.ulstu.deadline.model.Deadline; import ru.ulstu.paper.model.Paper; import ru.ulstu.paper.model.PaperDto; import ru.ulstu.paper.model.PaperFilterDto; +import ru.ulstu.paper.service.LatexService; import ru.ulstu.paper.service.PaperService; import ru.ulstu.user.model.User; import springfox.documentation.annotations.ApiIgnore; import javax.validation.Valid; import java.io.IOException; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.stream.Collectors; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.springframework.util.StringUtils.isEmpty; @@ -32,9 +38,11 @@ import static org.springframework.util.StringUtils.isEmpty; @ApiIgnore public class PaperController { private final PaperService paperService; + private final LatexService latexService; - public PaperController(PaperService paperService) { + public PaperController(PaperService paperService, LatexService latexService) { this.paperService = paperService; + this.latexService = latexService; } @GetMapping("/papers") @@ -51,7 +59,7 @@ public class PaperController { @GetMapping("/dashboard") public void getDashboard(ModelMap modelMap) { - modelMap.put("papers", paperService.findAllActive()); + modelMap.put("papers", paperService.findAllActiveDto()); } @GetMapping("/paper") @@ -97,6 +105,11 @@ public class PaperController { return paperService.getPaperStatuses(); } + @ModelAttribute("allTypes") + public List getPaperTypes() { + return paperService.getPaperTypes(); + } + @ModelAttribute("allAuthors") public List getAllAuthors() { return paperService.getPaperAuthors(); @@ -111,6 +124,14 @@ public class PaperController { return years; } + @PostMapping("/generatePdf") + public ResponseEntity getPdfFile(PaperDto paper) throws IOException, InterruptedException { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Disposition", "attachment; filename='" + + URLEncoder.encode(paper.getTitle() + ".pdf", UTF_8.toString()) + "'"); + return new ResponseEntity<>(latexService.generatePdfFromLatexFile(paper), headers, HttpStatus.OK); + } + private void filterEmptyDeadlines(PaperDto paperDto) { paperDto.setDeadlines(paperDto.getDeadlines().stream() .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) diff --git a/src/main/java/ru/ulstu/paper/controller/PaperRestController.java b/src/main/java/ru/ulstu/paper/controller/PaperRestController.java index 5dd6b9f..2bd8384 100644 --- a/src/main/java/ru/ulstu/paper/controller/PaperRestController.java +++ b/src/main/java/ru/ulstu/paper/controller/PaperRestController.java @@ -61,4 +61,9 @@ public class PaperRestController { public Response> filter(@RequestBody @Valid PaperFilterDto paperFilterDto) throws IOException { return new Response<>(paperService.filter(paperFilterDto)); } + + @GetMapping("formatted-list") + public Response> getFormattedPaperList() { + return new Response<>(paperService.getFormattedPaperList()); + } } diff --git a/src/main/java/ru/ulstu/paper/model/Paper.java b/src/main/java/ru/ulstu/paper/model/Paper.java index 623d3fb..4f77826 100644 --- a/src/main/java/ru/ulstu/paper/model/Paper.java +++ b/src/main/java/ru/ulstu/paper/model/Paper.java @@ -53,12 +53,32 @@ public class Paper extends BaseEntity implements UserContainer { } } + public enum PaperType { + OTHER("Прочая публикация"), + VAK("ВАК"), + SCOPUS("Scopus"), + WEB_OF_SCIENCE("Web Of Science"); + + private String typeName; + + PaperType(String name) { + this.typeName = name; + } + + public String getTypeName() { + return typeName; + } + } + @NotBlank private String title; @Enumerated(value = EnumType.STRING) private PaperStatus status = PaperStatus.DRAFT; + @Enumerated(value = EnumType.STRING) + private PaperType type = PaperType.OTHER; + @Column(name = "create_date") @Temporal(TemporalType.TIMESTAMP) private Date createDate = new Date(); @@ -91,6 +111,9 @@ public class Paper extends BaseEntity implements UserContainer { @ManyToMany(fetch = FetchType.EAGER) private Set authors = new HashSet<>(); + @Column(name = "latex_text") + private String latexText; + public PaperStatus getStatus() { return status; } @@ -99,6 +122,14 @@ public class Paper extends BaseEntity implements UserContainer { this.status = status; } + public PaperType getType() { + return type; + } + + public void setType(PaperType type) { + this.type = type; + } + public Date getCreateDate() { return createDate; } @@ -179,6 +210,14 @@ public class Paper extends BaseEntity implements UserContainer { this.url = url; } + public String getLatexText() { + return latexText; + } + + public void setLatexText(String latexText) { + this.latexText = latexText; + } + @Override public Set getUsers() { return getAuthors(); diff --git a/src/main/java/ru/ulstu/paper/model/PaperDto.java b/src/main/java/ru/ulstu/paper/model/PaperDto.java index da6f5d0..9ed60e7 100644 --- a/src/main/java/ru/ulstu/paper/model/PaperDto.java +++ b/src/main/java/ru/ulstu/paper/model/PaperDto.java @@ -3,8 +3,8 @@ package ru.ulstu.paper.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.StringUtils; -import ru.ulstu.file.model.FileDataDto; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.file.model.FileDataDto; import ru.ulstu.user.model.UserDto; import javax.validation.constraints.NotEmpty; @@ -25,6 +25,7 @@ public class PaperDto { @Size(min = 3, max = 254) private String title; private Paper.PaperStatus status; + private Paper.PaperType type; private Date createDate; private Date updateDate; @NotEmpty @@ -36,6 +37,7 @@ public class PaperDto { private Set authorIds; private Set authors; private Integer filterAuthorId; + private String latexText; public PaperDto() { deadlines.add(new Deadline()); @@ -45,10 +47,12 @@ public class PaperDto { public PaperDto(@JsonProperty("id") Integer id, @JsonProperty("title") String title, @JsonProperty("status") Paper.PaperStatus status, + @JsonProperty("type") Paper.PaperType type, @JsonProperty("createDate") Date createDate, @JsonProperty("updateDate") Date updateDate, @JsonProperty("deadlines") List deadlines, @JsonProperty("comment") String comment, + @JsonProperty("latex_text") String latexText, @JsonProperty("url") String url, @JsonProperty("locked") Boolean locked, @JsonProperty("files") List files, @@ -57,11 +61,13 @@ public class PaperDto { this.id = id; this.title = title; this.status = status; + this.type = type; this.createDate = createDate; this.updateDate = updateDate; this.deadlines = deadlines; this.comment = comment; this.url = url; + this.latexText = latexText; this.locked = locked; this.files = files; this.authors = authors; @@ -71,11 +77,13 @@ public class PaperDto { this.id = paper.getId(); this.title = paper.getTitle(); this.status = paper.getStatus(); + this.type = paper.getType(); this.createDate = paper.getCreateDate(); this.updateDate = paper.getUpdateDate(); this.deadlines = paper.getDeadlines(); this.comment = paper.getComment(); this.url = paper.getUrl(); + this.latexText = paper.getLatexText(); this.locked = paper.getLocked(); this.files = convert(paper.getFiles(), FileDataDto::new); this.authorIds = convert(paper.getAuthors(), user -> user.getId()); @@ -106,6 +114,14 @@ public class PaperDto { this.status = status; } + public Paper.PaperType getType() { + return type; + } + + public void setType(Paper.PaperType type) { + this.type = type; + } + public Date getCreateDate() { return createDate; } @@ -178,6 +194,14 @@ public class PaperDto { this.url = url; } + public String getLatexText() { + return latexText; + } + + public void setLatexText(String latexText) { + this.latexText = latexText; + } + public String getAuthorsString() { return StringUtils.abbreviate(authors .stream() diff --git a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java index 3951f53..343e9e2 100644 --- a/src/main/java/ru/ulstu/paper/repository/PaperRepository.java +++ b/src/main/java/ru/ulstu/paper/repository/PaperRepository.java @@ -12,4 +12,6 @@ public interface PaperRepository extends JpaRepository { @Query("SELECT p FROM Paper p WHERE (:author IS NULL OR :author MEMBER OF p.authors) AND (YEAR(p.createDate) = :year OR :year IS NULL)") List filter(@Param("author") User author, @Param("year") Integer year); + + List findByIdNotIn(List paperIds); } diff --git a/src/main/java/ru/ulstu/paper/service/LatexService.java b/src/main/java/ru/ulstu/paper/service/LatexService.java new file mode 100644 index 0000000..82dd1ba --- /dev/null +++ b/src/main/java/ru/ulstu/paper/service/LatexService.java @@ -0,0 +1,76 @@ +package ru.ulstu.paper.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.file.service.FileService; +import ru.ulstu.paper.model.PaperDto; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; + +@Service +public class LatexService { + private final String pdfLatexError = "Errors occurred while executing pdfLaTeX."; + private final String bibtexError = "Errors occurred while executing bibtex."; + private String errorMessage; + private File pdfFile; + private FileService fileService; + + public LatexService(FileService fileService) { + this.fileService = fileService; + } + + public byte[] generatePdfFromLatexFile(PaperDto paper) throws IOException, InterruptedException { + fileService.createLatexAttachs(paper); + File tex = fileService.createLatexFile(paper); + + if (!generate(paper.getTitle(), tex.getParentFile())) { + throw new IOException(errorMessage); + } + + return Files.readAllBytes(pdfFile.toPath()); + } + + private int startProcess(String[] args, File dir, String message) throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder(args); + processBuilder.redirectErrorStream(true); + processBuilder.directory(dir); + + Process process = processBuilder.start(); + InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream()); + + try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + while ((bufferedReader.readLine()) != null) { + // + } + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + errorMessage = message + " Exit value of the process: " + exitCode; + } + return exitCode; + } + + private boolean generate(String filename, File dir) throws IOException, InterruptedException { + startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError); + startProcess(new String[]{"bibtex", filename}, dir, bibtexError); + if (startProcess(new String[]{"pdflatex", filename, "--interaction=nonstopmode"}, dir, pdfLatexError) != 0) { + return false; + } + return checkPdf(filename, dir); + } + + private boolean checkPdf(String filename, File dir) { + pdfFile = new File(dir.getAbsolutePath() + File.separator + filename + ".pdf"); + + if (pdfFile.isFile()) { + return true; + } else { + errorMessage = "The pdf file could not be created."; + return false; + } + } +} diff --git a/src/main/java/ru/ulstu/paper/service/PaperService.java b/src/main/java/ru/ulstu/paper/service/PaperService.java index 328a52b..33344a5 100644 --- a/src/main/java/ru/ulstu/paper/service/PaperService.java +++ b/src/main/java/ru/ulstu/paper/service/PaperService.java @@ -21,6 +21,7 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; import static org.springframework.util.ObjectUtils.isEmpty; @@ -30,10 +31,12 @@ import static ru.ulstu.paper.model.Paper.PaperStatus.COMPLETED; import static ru.ulstu.paper.model.Paper.PaperStatus.DRAFT; import static ru.ulstu.paper.model.Paper.PaperStatus.FAILED; import static ru.ulstu.paper.model.Paper.PaperStatus.ON_PREPARATION; +import static ru.ulstu.paper.model.Paper.PaperType.OTHER; @Service public class PaperService { private final static int MAX_DISPLAY_SIZE = 40; + private final static String PAPER_FORMATTED_TEMPLATE = "%s %s"; private final PaperNotificationService paperNotificationService; private final PaperRepository paperRepository; @@ -66,13 +69,17 @@ public class PaperService { return papers; } - public List findAllActive() { - return findAllDto() + public List findAllActive() { + return findAll() .stream() .filter(paper -> paper.getStatus() != COMPLETED && paper.getStatus() != FAILED) .collect(toList()); } + public List findAllActiveDto() { + return convert(findAllActive(), PaperDto::new); + } + public PaperDto findOneDto(Integer id) { return new PaperDto(paperRepository.getOne(id)); } @@ -89,9 +96,11 @@ public class PaperService { private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException { paper.setComment(paperDto.getComment()); paper.setUrl(paperDto.getUrl()); + paper.setLatexText(paperDto.getLatexText()); paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate()); paper.setLocked(paperDto.getLocked()); paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus()); + paper.setType(paperDto.getType() == null ? OTHER : paperDto.getType()); paper.setTitle(paperDto.getTitle()); paper.setUpdateDate(new Date()); paper.setDeadlines(deadlineService.saveOrCreate(paperDto.getDeadlines())); @@ -142,6 +151,10 @@ public class PaperService { return Arrays.asList(Paper.PaperStatus.values()); } + public List getPaperTypes() { + return Arrays.asList(Paper.PaperType.values()); + } + @Transactional public Paper create(String title, User user, Date deadlineDate) { Paper paper = new Paper(); @@ -151,6 +164,7 @@ public class PaperService { paper.setCreateDate(new Date()); paper.setUpdateDate(new Date()); paper.setStatus(DRAFT); + paper.setType(OTHER); paper = paperRepository.save(paper); paperNotificationService.sendCreateNotification(paper); @@ -209,7 +223,41 @@ public class PaperService { return new PaperDto(paperRepository.getOne(paperId)); } + public Paper findEntityById(Integer paperId) { + return paperRepository.getOne(paperId); + } + + public List findAllNotSelect(List paperIds) { + if (!paperIds.isEmpty()) { + return sortPapers(paperRepository.findByIdNotIn(paperIds)); + } else { + return sortPapers(paperRepository.findAll()); + } + + } + public List getPaperAuthors() { return userService.findAll(); } + + public List getFormattedPaperList() { + return findAllCompleted() + .stream() + .map(paper -> String.format(PAPER_FORMATTED_TEMPLATE, paper.getTitle(), getAuthors(paper))) + .collect(toList()); + } + + private List findAllCompleted() { + return findAll() + .stream() + .filter(paper -> paper.getStatus() == COMPLETED) + .collect(toList()); + } + + private String getAuthors(Paper paper) { + return paper.getAuthors() + .stream() + .map(User::getUserAbbreviate) + .collect(Collectors.joining(", ")); + } } diff --git a/src/main/java/ru/ulstu/project/controller/ProjectController.java b/src/main/java/ru/ulstu/project/controller/ProjectController.java new file mode 100644 index 0000000..3244ccb --- /dev/null +++ b/src/main/java/ru/ulstu/project/controller/ProjectController.java @@ -0,0 +1,49 @@ +package ru.ulstu.project.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.ulstu.project.model.Project; +import ru.ulstu.project.model.ProjectDto; +import ru.ulstu.project.service.ProjectService; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.List; + +@Controller() +@RequestMapping(value = "/projects") +@ApiIgnore +public class ProjectController { + private final ProjectService projectService; + + public ProjectController(ProjectService projectService) { + this.projectService = projectService; + } + + @GetMapping("/dashboard") + public void getDashboard(ModelMap modelMap) { + modelMap.put("projects", projectService.findAllDto()); + } + + @GetMapping("/projects") + public void getProjects(ModelMap modelMap) { + modelMap.put("projects", projectService.findAllDto()); + } + + @GetMapping("/project") + public void getProject(ModelMap modelMap, @RequestParam(value = "id") Integer id) { + if (id != null && id > 0) { + modelMap.put("projectDto", projectService.findOneDto(id)); + } else { + modelMap.put("projectDto", new ProjectDto()); + } + } + + @ModelAttribute("allStatuses") + public List getProjectStatuses() { + return projectService.getProjectStatuses(); + } +} diff --git a/src/main/java/ru/ulstu/project/model/Project.java b/src/main/java/ru/ulstu/project/model/Project.java index 896c53d..16a8da6 100644 --- a/src/main/java/ru/ulstu/project/model/Project.java +++ b/src/main/java/ru/ulstu/project/model/Project.java @@ -2,25 +2,61 @@ package ru.ulstu.project.model; import ru.ulstu.core.model.BaseEntity; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.grant.model.Grant; import javax.persistence.CascadeType; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; @Entity public class Project extends BaseEntity { + public enum ProjectStatus { + APPLICATION("Заявка"), + ON_COMPETITION("Отправлен на конкурс"), + SUCCESSFUL_PASSAGE("Успешное прохождение"), + IN_WORK("В работе"), + COMPLETED("Завершен"), + FAILED("Провалены сроки"); + + private String statusName; + + ProjectStatus(String statusName) { + this.statusName = statusName; + } + + public String getStatusName() { + return statusName; + } + } @NotBlank private String title; + @Enumerated(value = EnumType.STRING) + private ProjectStatus status = ProjectStatus.APPLICATION; + + @NotNull + private String description; + @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "project_id") private List deadlines = new ArrayList<>(); + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "grant_id") + private Grant grant; + + @NotNull + private String repository; + public String getTitle() { return title; } @@ -29,6 +65,38 @@ public class Project extends BaseEntity { this.title = title; } + public ProjectStatus getStatus() { + return status; + } + + public void setStatus(ProjectStatus status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Grant getGrant() { + return grant; + } + + public void setGrant(Grant grant) { + this.grant = grant; + } + + public String getRepository() { + return repository; + } + + public void setRepository(String repository) { + this.repository = repository; + } + public List getDeadlines() { return deadlines; } diff --git a/src/main/java/ru/ulstu/project/model/ProjectDto.java b/src/main/java/ru/ulstu/project/model/ProjectDto.java index 65f4365..edde725 100644 --- a/src/main/java/ru/ulstu/project/model/ProjectDto.java +++ b/src/main/java/ru/ulstu/project/model/ProjectDto.java @@ -3,6 +3,7 @@ package ru.ulstu.project.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.grant.model.GrantDto; import javax.validation.constraints.NotEmpty; import java.util.ArrayList; @@ -13,8 +14,11 @@ public class ProjectDto { @NotEmpty private String title; - + private Project.ProjectStatus status; + private String description; private List deadlines = new ArrayList<>(); + private GrantDto grant; + private String repository; public ProjectDto() { } @@ -26,9 +30,17 @@ public class ProjectDto { @JsonCreator public ProjectDto(@JsonProperty("id") Integer id, @JsonProperty("title") String title, + @JsonProperty("status") Project.ProjectStatus status, + @JsonProperty("description") String description, + @JsonProperty("grant") GrantDto grant, + @JsonProperty("repository") String repository, @JsonProperty("deadlines") List deadlines) { this.id = id; this.title = title; + this.status = status; + this.description = description; + this.grant = grant; + this.repository = repository; this.deadlines = deadlines; } @@ -36,6 +48,10 @@ public class ProjectDto { public ProjectDto(Project project) { this.id = project.getId(); this.title = project.getTitle(); + this.status = project.getStatus(); + this.description = project.getDescription(); + this.grant = project.getGrant() == null ? null : new GrantDto(project.getGrant()); + this.repository = project.getRepository(); this.deadlines = project.getDeadlines(); } @@ -55,6 +71,38 @@ public class ProjectDto { this.title = title; } + public Project.ProjectStatus getStatus() { + return status; + } + + public void setStatus(Project.ProjectStatus status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public GrantDto getGrant() { + return grant; + } + + public void setGrant(GrantDto grant) { + this.grant = grant; + } + + public String getRepository() { + return repository; + } + + public void setRepository(String repository) { + this.repository = repository; + } + public List getDeadlines() { return deadlines; } diff --git a/src/main/java/ru/ulstu/project/service/ProjectService.java b/src/main/java/ru/ulstu/project/service/ProjectService.java index cbcc232..99edd69 100644 --- a/src/main/java/ru/ulstu/project/service/ProjectService.java +++ b/src/main/java/ru/ulstu/project/service/ProjectService.java @@ -2,17 +2,21 @@ package ru.ulstu.project.service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.thymeleaf.util.StringUtils; import ru.ulstu.deadline.service.DeadlineService; import ru.ulstu.project.model.Project; import ru.ulstu.project.model.ProjectDto; import ru.ulstu.project.repository.ProjectRepository; +import java.util.Arrays; import java.util.List; import static org.springframework.util.ObjectUtils.isEmpty; +import static ru.ulstu.core.util.StreamApiUtils.convert; @Service public class ProjectService { + private final static int MAX_DISPLAY_SIZE = 40; private final ProjectRepository projectRepository; private final DeadlineService deadlineService; @@ -27,6 +31,20 @@ public class ProjectService { return projectRepository.findAll(); } + public List findAllDto() { + List projects = convert(findAll(), ProjectDto::new); + projects.forEach(projectDto -> projectDto.setTitle(StringUtils.abbreviate(projectDto.getTitle(), MAX_DISPLAY_SIZE))); + return projects; + } + + public ProjectDto findOneDto(Integer id) { + return new ProjectDto(projectRepository.getOne(id)); + } + + public List getProjectStatuses() { + return Arrays.asList(Project.ProjectStatus.values()); + } + @Transactional public Project create(ProjectDto projectDto) { Project newProject = copyFromDto(new Project(), projectDto); diff --git a/src/main/java/ru/ulstu/students/controller/Navigation.java b/src/main/java/ru/ulstu/students/controller/Navigation.java new file mode 100644 index 0000000..3804a8b --- /dev/null +++ b/src/main/java/ru/ulstu/students/controller/Navigation.java @@ -0,0 +1,16 @@ +package ru.ulstu.students.controller; + +import org.springframework.validation.Errors; + +public class Navigation { + public static final String REDIRECT_TO = "redirect:%s"; + public static final String TASKS_PAGE = "/students/tasks"; + public static final String TASK_PAGE = "/students/task"; + + public static String hasErrors(Errors errors, String page) { + if (errors.hasErrors()) { + return page; + } + return null; + } +} diff --git a/src/main/java/ru/ulstu/students/controller/TaskController.java b/src/main/java/ru/ulstu/students/controller/TaskController.java new file mode 100644 index 0000000..50cb052 --- /dev/null +++ b/src/main/java/ru/ulstu/students/controller/TaskController.java @@ -0,0 +1,90 @@ +package ru.ulstu.students.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.Errors; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.ulstu.deadline.model.Deadline; +import ru.ulstu.students.model.Task; +import ru.ulstu.students.model.TaskDto; +import ru.ulstu.students.service.TaskService; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.Valid; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import static org.springframework.util.StringUtils.isEmpty; +import static ru.ulstu.students.controller.Navigation.REDIRECT_TO; +import static ru.ulstu.students.controller.Navigation.TASKS_PAGE; +import static ru.ulstu.students.controller.Navigation.TASK_PAGE; + +@Controller() +@RequestMapping(value = "/students") +@ApiIgnore +public class TaskController { + + private final TaskService taskService; + + public TaskController(TaskService taskService) { + this.taskService = taskService; + } + + @GetMapping("/tasks") + public void getTasks(ModelMap modelMap) { + modelMap.put("tasks", taskService.findAllDto()); + } + + @GetMapping("/dashboard") + public void getDashboard(ModelMap modelMap) { + modelMap.put("tasks", taskService.findAllDto()); + } + + @GetMapping("/task") + public void getTask(ModelMap modelMap, @RequestParam(value = "id") Integer id) { + if (id != null && id > 0) { + modelMap.put("taskDto", taskService.findOneDto(id)); + } else { + modelMap.put("taskDto", new TaskDto()); + } + } + + @PostMapping(value = "/task", params = "save") + public String save(@Valid TaskDto taskDto, Errors errors) throws IOException { + filterEmptyDeadlines(taskDto); + if (taskDto.getDeadlines().isEmpty()) { + errors.rejectValue("deadlines", "errorCode", "Не может быть пустым"); + } + if (errors.hasErrors()) { + return TASK_PAGE; + } + taskService.save(taskDto); + return String.format(REDIRECT_TO, TASKS_PAGE); + } + + @PostMapping(value = "/task", params = "addDeadline") + public String addDeadline(@Valid TaskDto taskDto, Errors errors) { + filterEmptyDeadlines(taskDto); + if (errors.hasErrors()) { + return TASK_PAGE; + } + taskDto.getDeadlines().add(new Deadline()); + return TASK_PAGE; + } + + @ModelAttribute("allStatuses") + public List getTaskStatuses() { + return taskService.getTaskStatuses(); + } + + private void filterEmptyDeadlines(TaskDto taskDto) { + taskDto.setDeadlines(taskDto.getDeadlines().stream() + .filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription())) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/ru/ulstu/students/model/Task.java b/src/main/java/ru/ulstu/students/model/Task.java new file mode 100644 index 0000000..c4d7409 --- /dev/null +++ b/src/main/java/ru/ulstu/students/model/Task.java @@ -0,0 +1,130 @@ +package ru.ulstu.students.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.deadline.model.Deadline; +import ru.ulstu.tags.model.Tag; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Entity +public class Task extends BaseEntity { + + public enum TaskStatus { + IN_WORK("В работе"), + COMPLETED("Завершен"), + FAILED("Провалены сроки"), + LOADED_FROM_KIAS("Загружен автоматически"); + + private String statusName; + + TaskStatus(String name) { + this.statusName = name; + } + + public String getStatusName() { + return statusName; + } + } + + @NotBlank + private String title; + + private String description; + + @Enumerated(value = EnumType.STRING) + private ru.ulstu.students.model.Task.TaskStatus status = TaskStatus.IN_WORK; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @JoinColumn(name = "task_id", unique = true) + @Fetch(FetchMode.SUBSELECT) + @OrderBy("date") + private List deadlines = new ArrayList<>(); + + @Column(name = "create_date") + @Temporal(TemporalType.TIMESTAMP) + private Date createDate = new Date(); + + @Column(name = "update_date") + @Temporal(TemporalType.TIMESTAMP) + private Date updateDate = new Date(); + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "task_tags", + joinColumns = {@JoinColumn(name = "task_id")}, + inverseJoinColumns = {@JoinColumn(name = "tag_id")}) + private List tags = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public TaskStatus getStatus() { + return status; + } + + public void setStatus(TaskStatus status) { + this.status = status; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public Date getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } +} diff --git a/src/main/java/ru/ulstu/students/model/TaskDto.java b/src/main/java/ru/ulstu/students/model/TaskDto.java new file mode 100644 index 0000000..941a6df --- /dev/null +++ b/src/main/java/ru/ulstu/students/model/TaskDto.java @@ -0,0 +1,144 @@ +package ru.ulstu.students.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +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.tags.model.Tag; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class TaskDto { + + private final static int MAX_TAGS_LENGTH = 50; + + private Integer id; + @NotEmpty + private String title; + private String description; + private Task.TaskStatus status; + private List deadlines = new ArrayList<>(); + private Date createDate; + private Date updateDate; + private Set tagIds; + private List tags; + + public TaskDto() { + deadlines.add(new Deadline()); + } + + @JsonCreator + public TaskDto(@JsonProperty("id") Integer id, + @JsonProperty("title") String title, + @JsonProperty("description") String description, + @JsonProperty("createDate") Date createDate, + @JsonProperty("updateDate") Date updateDate, + @JsonProperty("status") Task.TaskStatus status, + @JsonProperty("deadlines") List deadlines, + @JsonProperty("tagIds") Set tagIds, + @JsonProperty("tags") List tags) { + this.id = id; + this.title = title; + this.status = status; + this.deadlines = deadlines; + this.createDate = createDate; + this.updateDate = updateDate; + this.description = description; + this.tags = tags; + } + + public TaskDto(Task task) { + this.id = task.getId(); + this.title = task.getTitle(); + this.status = task.getStatus(); + this.deadlines = task.getDeadlines(); + this.createDate = task.getCreateDate(); + this.updateDate = task.getUpdateDate(); + this.description = task.getDescription(); + this.tags = task.getTags(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Task.TaskStatus getStatus() { + return status; + } + + public void setStatus(Task.TaskStatus status) { + this.status = status; + } + + public List getDeadlines() { + return deadlines; + } + + public void setDeadlines(List deadlines) { + this.deadlines = deadlines; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public Date getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + } + + public Set getTagIds() { + return tagIds; + } + + public void setTagIds(Set tagIds) { + this.tagIds = tagIds; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public String getTagsString() { + return StringUtils.abbreviate(tags + .stream() + .map(tag -> tag.getTagName()) + .collect(Collectors.joining(", ")), MAX_TAGS_LENGTH); + } +} diff --git a/src/main/java/ru/ulstu/students/repository/TaskRepository.java b/src/main/java/ru/ulstu/students/repository/TaskRepository.java new file mode 100644 index 0000000..6f48d1f --- /dev/null +++ b/src/main/java/ru/ulstu/students/repository/TaskRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.students.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.students.model.Task; + +public interface TaskRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/students/service/TaskService.java b/src/main/java/ru/ulstu/students/service/TaskService.java new file mode 100644 index 0000000..dac6ef5 --- /dev/null +++ b/src/main/java/ru/ulstu/students/service/TaskService.java @@ -0,0 +1,95 @@ +package ru.ulstu.students.service; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.deadline.service.DeadlineService; +import ru.ulstu.students.model.Task; +import ru.ulstu.students.model.TaskDto; +import ru.ulstu.students.repository.TaskRepository; +import ru.ulstu.tags.service.TagService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import static org.springframework.util.ObjectUtils.isEmpty; +import static ru.ulstu.core.util.StreamApiUtils.convert; +import static ru.ulstu.students.model.Task.TaskStatus.IN_WORK; + +@Service +public class TaskService { + + private final static int MAX_DISPLAY_SIZE = 40; + + private final TaskRepository taskRepository; + private final DeadlineService deadlineService; + private final TagService tagService; + + public TaskService(TaskRepository grantRepository, + DeadlineService deadlineService, TagService tagService) { + this.taskRepository = grantRepository; + this.deadlineService = deadlineService; + this.tagService = tagService; + } + + public List findAll() { + return taskRepository.findAll(); + } + + public List findAllDto() { + List tasks = convert(findAll(), TaskDto::new); + tasks.forEach(taskDto -> taskDto.setTitle(StringUtils.abbreviate(taskDto.getTitle(), MAX_DISPLAY_SIZE))); + return tasks; + } + + public TaskDto findOneDto(Integer id) { + return new TaskDto(taskRepository.getOne(id)); + } + + @Transactional + public Integer create(TaskDto taskDto) throws IOException { + Task newTask = copyFromDto(new Task(), taskDto); + newTask = taskRepository.save(newTask); + return newTask.getId(); + } + + private Task copyFromDto(Task task, TaskDto taskDto) throws IOException { + task.setTitle(taskDto.getTitle()); + task.setDescription(taskDto.getDescription()); + task.setStatus(taskDto.getStatus() == null ? IN_WORK : taskDto.getStatus()); + task.setDeadlines(deadlineService.saveOrCreate(taskDto.getDeadlines())); + task.setCreateDate(task.getCreateDate() == null ? new Date() : task.getCreateDate()); + task.setUpdateDate(new Date()); + task.getTags().clear(); + task.setTags(tagService.saveOrCreate(taskDto.getTags())); + return task; + } + + @Transactional + public Integer update(TaskDto taskDto) throws IOException { + Task task = taskRepository.getOne(taskDto.getId()); + taskRepository.save(copyFromDto(task, taskDto)); + return task.getId(); + } + + @Transactional + public void delete(Integer taskId) throws IOException { + Task task = taskRepository.getOne(taskId); + taskRepository.delete(task); + } + + public void save(TaskDto taskDto) throws IOException { + if (isEmpty(taskDto.getId())) { + create(taskDto); + } else { + update(taskDto); + } + } + + public List getTaskStatuses() { + return Arrays.asList(Task.TaskStatus.values()); + } + +} diff --git a/src/main/java/ru/ulstu/tags/model/Tag.java b/src/main/java/ru/ulstu/tags/model/Tag.java new file mode 100644 index 0000000..aed000f --- /dev/null +++ b/src/main/java/ru/ulstu/tags/model/Tag.java @@ -0,0 +1,44 @@ +package ru.ulstu.tags.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.NotEmpty; +import ru.ulstu.core.model.BaseEntity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +@Entity +@Table(name = "tag") +public class Tag extends BaseEntity { + + @NotEmpty + @Size(max = 50) + @Column(name = "tag_name") + private String tagName; + + public Tag() { + + } + + @JsonCreator + public Tag(@JsonProperty("id") Integer id, + @JsonProperty("tag_name") String tagName) { + this.setId(id); + this.tagName = tagName; + } + + public Tag(String name) { + this.tagName = name; + } + + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; + } +} diff --git a/src/main/java/ru/ulstu/tags/repository/TagRepository.java b/src/main/java/ru/ulstu/tags/repository/TagRepository.java new file mode 100644 index 0000000..213abae --- /dev/null +++ b/src/main/java/ru/ulstu/tags/repository/TagRepository.java @@ -0,0 +1,12 @@ +package ru.ulstu.tags.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.tags.model.Tag; + +public interface TagRepository extends JpaRepository { + + @Query("SELECT t FROM Tag t WHERE (t.tagName = :tagName)") + Tag findByName(@Param("tagName") String tagName); +} diff --git a/src/main/java/ru/ulstu/tags/service/TagService.java b/src/main/java/ru/ulstu/tags/service/TagService.java new file mode 100644 index 0000000..ad5d227 --- /dev/null +++ b/src/main/java/ru/ulstu/tags/service/TagService.java @@ -0,0 +1,53 @@ +package ru.ulstu.tags.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.tags.model.Tag; +import ru.ulstu.tags.repository.TagRepository; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class TagService { + + private final TagRepository tagRepository; + + + public TagService(TagRepository tagRepository) { + + this.tagRepository = tagRepository; + } + + public List saveOrCreate(List tags) { + return tags + .stream() + .map(tag -> { + if (tag.getId() != null) { + return getExistById(tag); + } else { + Tag existTag = isExistByName(tag.getTagName()); + return existTag != null ? existTag : create(tag); + } + }).collect(Collectors.toList()); + } + + @Transactional + public Tag getExistById(Tag tag) { + return tagRepository.getOne(tag.getId()); + } + + @Transactional + public Tag isExistByName(String tagName) { + return tagRepository.findByName(tagName); + } + + @Transactional + public Tag create(Tag tag) { + Tag newTag = new Tag(); + newTag.setTagName(tag.getTagName()); + newTag = tagRepository.save(newTag); + return newTag; + } + +} diff --git a/src/main/java/ru/ulstu/timeline/service/EventService.java b/src/main/java/ru/ulstu/timeline/service/EventService.java index 2a8858f..a1f54f5 100644 --- a/src/main/java/ru/ulstu/timeline/service/EventService.java +++ b/src/main/java/ru/ulstu/timeline/service/EventService.java @@ -124,7 +124,7 @@ public class EventService { } public void updatePaperDeadlines(Paper paper) { - eventRepository.delete(eventRepository.findAllByPaper(paper)); + eventRepository.deleteAll(eventRepository.findAllByPaper(paper)); createFromPaper(paper); } diff --git a/src/main/java/ru/ulstu/user/model/User.java b/src/main/java/ru/ulstu/user/model/User.java index de3be9e..fcfd88e 100644 --- a/src/main/java/ru/ulstu/user/model/User.java +++ b/src/main/java/ru/ulstu/user/model/User.java @@ -6,6 +6,8 @@ import ru.ulstu.core.model.BaseEntity; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; @@ -24,6 +26,8 @@ import java.util.Set; @Entity @Table(name = "users") public class User extends BaseEntity { + private final static String USER_ABBREVIATE_TEMPLATE = "%s %s%s"; + @NotNull @Pattern(regexp = Constants.LOGIN_REGEX) @Size(min = 1, max = 50) @@ -83,6 +87,28 @@ public class User extends BaseEntity { @BatchSize(size = 20) private Set 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() { roles = new HashSet<>(); activated = false; @@ -186,4 +212,27 @@ public class User extends BaseEntity { public void setPatronymic(String 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() { + return String.format(USER_ABBREVIATE_TEMPLATE, + lastName == null ? "" : lastName, + firstName == null ? "" : firstName.substring(0, 1) + ".", + patronymic == null ? "" : patronymic.substring(0, 1) + "."); + } } diff --git a/src/main/java/ru/ulstu/user/model/UserDto.java b/src/main/java/ru/ulstu/user/model/UserDto.java index ecb8bbe..c3f8502 100644 --- a/src/main/java/ru/ulstu/user/model/UserDto.java +++ b/src/main/java/ru/ulstu/user/model/UserDto.java @@ -15,6 +15,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import java.util.Collection; +import java.util.Date; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -70,6 +71,10 @@ public class UserDto implements OdinDto { @Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50) private String passwordConfirm; + private Date birthDate; + + private User.UserDegree degree; + public UserDto() { activated = false; roles = new LinkedHashSet<>(); @@ -86,6 +91,8 @@ public class UserDto implements OdinDto { this.roles.addAll(user.getRoles().stream() .map(UserRoleDto::new) .collect(Collectors.toList())); + this.birthDate = user.getBirthDate(); + this.degree = user.getDegree(); } public Integer getId() { @@ -163,6 +170,22 @@ public class UserDto implements OdinDto { 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 public boolean isPasswordsValid() { if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) { @@ -188,6 +211,8 @@ public class UserDto implements OdinDto { ", roles=" + roles + ", password='" + password + '\'' + ", passwordConfirm='" + passwordConfirm + '\'' + + ", birthDate='" + birthDate + '\'' + + ", degree='" + degree + '\'' + '}'; } } diff --git a/src/main/java/ru/ulstu/user/model/UserSession.java b/src/main/java/ru/ulstu/user/model/UserSession.java index 1eb761d..50fec65 100644 --- a/src/main/java/ru/ulstu/user/model/UserSession.java +++ b/src/main/java/ru/ulstu/user/model/UserSession.java @@ -2,7 +2,13 @@ package ru.ulstu.user.model; import ru.ulstu.core.model.BaseEntity; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import javax.validation.constraints.NotNull; import java.util.Date; diff --git a/src/main/java/ru/ulstu/user/repository/UserRepository.java b/src/main/java/ru/ulstu/user/repository/UserRepository.java index 2edc8aa..afcdd69 100644 --- a/src/main/java/ru/ulstu/user/repository/UserRepository.java +++ b/src/main/java/ru/ulstu/user/repository/UserRepository.java @@ -2,6 +2,8 @@ package ru.ulstu.user.repository; import org.springframework.data.jpa.repository.EntityGraph; 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 java.util.Date; @@ -25,4 +27,11 @@ public interface UserRepository extends JpaRepository { @EntityGraph(attributePaths = "roles") 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 filterByAgeAndDegree(@Param("hasAge") boolean hasAge, + @Param("hasDegree") boolean hasDegree); } diff --git a/src/main/java/ru/ulstu/user/service/UserService.java b/src/main/java/ru/ulstu/user/service/UserService.java index 198ffcd..f92d2e3 100644 --- a/src/main/java/ru/ulstu/user/service/UserService.java +++ b/src/main/java/ru/ulstu/user/service/UserService.java @@ -325,4 +325,8 @@ public class UserService implements UserDetailsService { } return user; } + + public List filterByAgeAndDegree(boolean hasDegree, boolean hasAge) { + return userRepository.filterByAgeAndDegree(hasDegree, hasAge); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 75bd2a4..75c8ef9 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -34,4 +34,5 @@ spring.liquibase.enabled=true ng-tracker.base-url=http://127.0.0.1:8080 ng-tracker.undead-user-login=admin ng-tracker.dev-mode=true -ng-tracker.use-https=false \ No newline at end of file +ng-tracker.use-https=false +ng-tracker.check-run=false \ No newline at end of file diff --git a/src/main/resources/db/changelog-20190323_000001-schema.xml b/src/main/resources/db/changelog-20190323_000001-schema.xml new file mode 100644 index 0000000..1ab3efb --- /dev/null +++ b/src/main/resources/db/changelog-20190323_000001-schema.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190402_000000-schema.xml b/src/main/resources/db/changelog-20190402_000000-schema.xml new file mode 100644 index 0000000..1bbb256 --- /dev/null +++ b/src/main/resources/db/changelog-20190402_000000-schema.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190404_000000-schema.xml b/src/main/resources/db/changelog-20190404_000000-schema.xml new file mode 100644 index 0000000..0f45e31 --- /dev/null +++ b/src/main/resources/db/changelog-20190404_000000-schema.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190410_000000-schema.xml b/src/main/resources/db/changelog-20190410_000000-schema.xml new file mode 100644 index 0000000..b8db7f9 --- /dev/null +++ b/src/main/resources/db/changelog-20190410_000000-schema.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-20190417_000000-schema.xml b/src/main/resources/db/changelog-20190417_000000-schema.xml new file mode 100644 index 0000000..f0a0d64 --- /dev/null +++ b/src/main/resources/db/changelog-20190417_000000-schema.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-20190418_000000-schema.xml b/src/main/resources/db/changelog-20190418_000000-schema.xml new file mode 100644 index 0000000..31cf21c --- /dev/null +++ b/src/main/resources/db/changelog-20190418_000000-schema.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-20190421_000000-schema.xml b/src/main/resources/db/changelog-20190421_000000-schema.xml new file mode 100644 index 0000000..b7fc374 --- /dev/null +++ b/src/main/resources/db/changelog-20190421_000000-schema.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index 800b2f1..7b7e13a 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -23,5 +23,13 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/common/changelog-20190423_100000-schema.xml b/src/main/resources/db/common/changelog-20190423_100000-schema.xml new file mode 100644 index 0000000..602014e --- /dev/null +++ b/src/main/resources/db/common/changelog-20190423_100000-schema.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/drivers/chromedriver b/src/main/resources/drivers/chromedriver new file mode 100644 index 0000000..02ff671 Binary files /dev/null and b/src/main/resources/drivers/chromedriver differ diff --git a/src/main/resources/drivers/chromedriver.exe b/src/main/resources/drivers/chromedriver.exe new file mode 100644 index 0000000..28a4067 Binary files /dev/null and b/src/main/resources/drivers/chromedriver.exe differ diff --git a/src/main/resources/drivers/geckodriver b/src/main/resources/drivers/geckodriver new file mode 100644 index 0000000..ba1da8c Binary files /dev/null and b/src/main/resources/drivers/geckodriver differ diff --git a/src/main/resources/drivers/geckodriver.exe b/src/main/resources/drivers/geckodriver.exe new file mode 100644 index 0000000..6208aa8 Binary files /dev/null and b/src/main/resources/drivers/geckodriver.exe differ diff --git a/src/main/resources/public/css/agency.css b/src/main/resources/public/css/agency.css index 24680aa..fb3ec24 100644 --- a/src/main/resources/public/css/agency.css +++ b/src/main/resources/public/css/agency.css @@ -1,839 +1,839 @@ -body { - overflow-x: hidden; - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -p { - line-height: 1.75; -} - -a { - color: #000000; -} - -a:hover { - color: #fec503; -} - -.text-draft { - color: rgba(0, 0, 0, 0.48) !important; -} - -.text-primary { - color: #228bba !important; -} - -.text-warning { - color: #940000 !important; -} - -.text-review { - color: #94028d !important; -} - -.text-success { - color: #007741 !important; -} - -.text-accepted { - color: #fec503 !important; -} - -.text-not-accepted { - color: #A38831 !important; -} - -.text-failed { - color: #A38831 !important; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 700; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section { - padding: 100px 0; -} - -section h2.section-heading { - font-size: 2.5vw; - margin-top: 0; - margin-bottom: 15px; -} - -section h3.section-subheading { - font-size: 16px; - font-weight: 400; - font-style: italic; - margin-bottom: 75px; - text-transform: none; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -@media (min-width: 768px) { - section { - padding: 100px 0; - } -} - -.btn { - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; -} - -.btn-xl { - font-size: 18px; - padding: 20px 40px; -} - -.btn-success { - background-color: #28a745; - border-color: #28a745; -} - -.btn-primary { - background-color: #fed136; - border-color: #fed136; -} - -.btn-default { - background-color: #ffffff; - border-color: #ced4da;; -} - -.btn-info { - background-color: #5bc0de; - border-color: #5bc0de; -} - -.progress-bar { - display: flex; - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #fed136; - transition: width .6s ease; -} - -.btn-primary:active, .btn-primary:focus, .btn-primary:hover { - background-color: #fec810 !important; - border-color: #fec810 !important; - color: white; -} - -.btn-primary:active, .btn-primary:focus { - box-shadow: 0 0 0 0.2rem rgba(254, 209, 55, 0.5) !important; -} - -::-moz-selection { - background: #fed136; - text-shadow: none; -} - -::selection { - background: #fed136; - text-shadow: none; -} - -img::selection { - background: transparent; -} - -img::-moz-selection { - background: transparent; -} - -#mainNav { - background-color: #212529; -} - -#mainNav .navbar-toggler { - font-size: 12px; - right: 0; - padding: 13px; - text-transform: uppercase; - color: white; - border: 0; - background-color: #fed136; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#mainNav .navbar-brand { - color: #fed136; - font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive; -} - -#mainNav .navbar-brand.active, #mainNav .navbar-brand:active, #mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover { - color: #fec503; -} - -#mainNav .navbar-nav .nav-item .nav-link { - font-size: 90%; - font-weight: 400; - padding: 0.75em 0; - letter-spacing: 1px; - color: white; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#mainNav .navbar-nav .nav-item .nav-link.active, #mainNav .navbar-nav .nav-item .nav-link:hover { - color: #fed136; -} - -@media (min-width: 992px) { - #mainNav { - padding-top: 25px; - padding-bottom: 25px; - -webkit-transition: padding-top 0.3s, padding-bottom 0.3s; - -moz-transition: padding-top 0.3s, padding-bottom 0.3s; - transition: padding-top 0.3s, padding-bottom 0.3s; - border: none; - background-color: transparent; - } - - #mainNav .navbar-brand { - font-size: 1.75em; - -webkit-transition: all 0.3s; - -moz-transition: all 0.3s; - transition: all 0.3s; - } - - #mainNav .navbar-nav .nav-item .nav-link { - padding: 1.1em 1em !important; - } - - #mainNav.navbar-shrink { - padding-top: 0; - padding-bottom: 0; - background-color: #212529; - } - - #mainNav.navbar-shrink .navbar-brand { - font-size: 1.25em; - padding: 12px 0; - } -} - -header.masthead { - text-align: center; - color: white; - background-image: url("../img/header-bg.jpg"); - background-repeat: no-repeat; - background-attachment: scroll; - background-position: center center; - -webkit-background-size: cover; - -moz-background-size: cover; - -o-background-size: cover; - background-size: cover; -} - -header.masthead .intro-text { - padding-top: 150px; - padding-bottom: 100px; -} - -header.masthead .intro-text .intro-lead-in { - font-size: 22px; - font-style: italic; - line-height: 22px; - margin-bottom: 25px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -header.masthead .intro-text .intro-heading { - font-size: 50px; - font-weight: 700; - line-height: 50px; - margin-bottom: 25px; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -@media (min-width: 768px) { - header.masthead .intro-text { - padding-top: 300px; - padding-bottom: 200px; - } - - header.masthead .intro-text .intro-lead-in { - font-size: 40px; - font-style: italic; - line-height: 40px; - margin-bottom: 25px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; - } - - header.masthead .intro-text .intro-heading { - font-size: 75px; - font-weight: 700; - line-height: 75px; - margin-bottom: 50px; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; - } -} - -.service-heading { - margin: 15px 0; - text-transform: none; -} - -#portfolio .portfolio-item { - right: 0; - margin: 0 0 15px; -} - -#portfolio .portfolio-item .portfolio-link { - position: relative; - display: block; - max-width: 400px; - margin: 0 auto; - cursor: pointer; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover { - position: absolute; - width: 100%; - height: 100%; - -webkit-transition: all ease 0.5s; - -moz-transition: all ease 0.5s; - transition: all ease 0.5s; - opacity: 0; - background: rgba(254, 209, 54, 0.9); -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { - opacity: 1; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content { - font-size: 20px; - position: absolute; - top: 50%; - width: 100%; - height: 20px; - margin-top: -12px; - text-align: center; - color: white; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i { - margin-top: -12px; -} - -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3, -#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 { - margin: 0; -} - -#portfolio .portfolio-item .portfolio-caption { - max-width: 400px; - margin: 0 auto; - padding: 25px; - text-align: center; - background-color: #fff; -} - -#portfolio .portfolio-item .portfolio-caption h4 { - margin: 0; - text-transform: none; -} - -#portfolio .portfolio-item .portfolio-caption p { - font-size: 16px; - font-style: italic; - margin: 0; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -#portfolio * { - z-index: 2; -} - -@media (min-width: 767px) { - #portfolio .portfolio-item { - margin: 0 0 30px; - } -} - -.portfolio-modal { - padding-right: 0px !important; -} - -.portfolio-modal .modal-dialog { - margin: 1rem; - max-width: 100vw; -} - -.portfolio-modal .modal-content { - padding: 100px 0; - text-align: center; -} - -.portfolio-modal .modal-content h2 { - font-size: 3em; - margin-bottom: 15px; -} - -.portfolio-modal .modal-content p { - margin-bottom: 30px; -} - -.portfolio-modal .modal-content p.item-intro { - font-size: 16px; - font-style: italic; - margin: 20px 0 30px; - font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -.portfolio-modal .modal-content ul.list-inline { - margin-top: 0; - margin-bottom: 30px; -} - -.portfolio-modal .modal-content img { - margin-bottom: 30px; -} - -.portfolio-modal .modal-content button { - cursor: pointer; -} - -.portfolio-modal .close-modal { - position: absolute; - top: 25px; - right: 25px; - width: 75px; - height: 75px; - cursor: pointer; - background-color: transparent; -} - -.portfolio-modal .close-modal:hover { - opacity: 0.3; -} - -.portfolio-modal .close-modal .lr { - /* Safari and Chrome */ - z-index: 1051; - width: 1px; - height: 75px; - margin-left: 35px; - /* IE 9 */ - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - background-color: #212529; -} - -.portfolio-modal .close-modal .lr .rl { - /* Safari and Chrome */ - z-index: 1052; - width: 1px; - height: 75px; - /* IE 9 */ - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); - background-color: #212529; -} - -.timeline { - position: relative; - padding: 0; - list-style: none; -} - -.timeline:before { - position: absolute; - top: 0; - bottom: 0; - left: 40px; - width: 2px; - margin-left: -1.5px; - content: ''; - background-color: #e9ecef; -} - -.timeline > li { - position: relative; - min-height: 50px; - margin-bottom: 50px; -} - -.timeline > li:after, .timeline > li:before { - display: table; - content: ' '; -} - -.timeline > li:after { - clear: both; -} - -.timeline > li .timeline-panel { - position: relative; - float: right; - width: 100%; - padding: 0 20px 0 100px; - text-align: left; -} - -.timeline > li .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; -} - -.timeline > li .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; -} - -.timeline > li .timeline-image { - position: absolute; - z-index: 100; - left: 0; - width: 80px; - height: 80px; - margin-left: 0; - text-align: center; - color: white; - border: 7px solid #e9ecef; - border-radius: 100%; - background-color: #fed136; -} - -.timeline > li .timeline-image h4 { - font-size: 10px; - line-height: 14px; - margin-top: 12px; -} - -.timeline > li.timeline-inverted > .timeline-panel { - float: right; - padding: 0 20px 0 100px; - text-align: left; -} - -.timeline > li.timeline-inverted > .timeline-panel:before { - right: auto; - left: -15px; - border-right-width: 15px; - border-left-width: 0; -} - -.timeline > li.timeline-inverted > .timeline-panel:after { - right: auto; - left: -14px; - border-right-width: 14px; - border-left-width: 0; -} - -.timeline > li:last-child { - margin-bottom: 0; -} - -.timeline .timeline-heading h4 { - margin-top: 0; - color: inherit; -} - -.timeline .timeline-heading h4.subheading { - text-transform: none; -} - -.timeline .timeline-body > ul, -.timeline .timeline-body > p { - margin-bottom: 0; -} - -@media (min-width: 768px) { - .timeline:before { - left: 50%; - } - - .timeline > li { - min-height: 100px; - margin-bottom: 100px; - } - - .timeline > li .timeline-panel { - float: left; - width: 41%; - padding: 0 20px 20px 30px; - text-align: right; - } - - .timeline > li .timeline-image { - left: 50%; - width: 100px; - height: 100px; - margin-left: -50px; - } - - .timeline > li .timeline-image h4 { - font-size: 13px; - line-height: 18px; - margin-top: 16px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - float: right; - padding: 0 30px 20px 20px; - text-align: left; - } -} - -@media (min-width: 992px) { - .timeline > li { - min-height: 150px; - } - - .timeline > li .timeline-panel { - padding: 0 20px 20px; - } - - .timeline > li .timeline-image { - width: 150px; - height: 150px; - margin-left: -75px; - } - - .timeline > li .timeline-image h4 { - font-size: 18px; - line-height: 26px; - margin-top: 30px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - padding: 0 20px 20px; - } -} - -@media (min-width: 1200px) { - .timeline > li { - min-height: 170px; - } - - .timeline > li .timeline-panel { - padding: 0 20px 20px 100px; - } - - .timeline > li .timeline-image { - width: 170px; - height: 170px; - margin-left: -85px; - } - - .timeline > li .timeline-image h4 { - margin-top: 40px; - } - - .timeline > li.timeline-inverted > .timeline-panel { - padding: 0 100px 20px 20px; - } -} - -.team-member { - margin-bottom: 50px; - text-align: center; -} - -.team-member img { - width: 225px; - height: 225px; - border: 7px solid #fff; -} - -.team-member h4 { - margin-top: 25px; - margin-bottom: 0; - text-transform: none; -} - -.team-member p { - margin-top: 0; -} - -section#contact { - background-color: #212529; - background-image: url("../img/map-image.png"); - background-repeat: no-repeat; - background-position: center; -} - -section#contact .section-heading { - color: #fff; -} - -section#contact .form-group { - margin-bottom: 25px; -} - -section#contact .form-group input, -section#contact .form-group textarea { - padding: 20px; -} - -section#contact .form-group input.form-control { - height: auto; -} - -section#contact .form-group textarea.form-control { - height: 248px; -} - -section#contact .form-control:focus { - border-color: #fed136; - box-shadow: none; -} - -section#contact ::-webkit-input-placeholder { - font-weight: 700; - color: #ced4da; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact :-moz-placeholder { - font-weight: 700; - color: #ced4da; - /* Firefox 18- */ - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact ::-moz-placeholder { - font-weight: 700; - color: #ced4da; - /* Firefox 19+ */ - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -section#contact :-ms-input-placeholder { - font-weight: 700; - color: #ced4da; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -footer { - padding: 25px 0; - text-align: center; -} - -footer span.copyright { - font-size: 90%; - line-height: 40px; - text-transform: none; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -footer ul.quicklinks { - font-size: 90%; - line-height: 40px; - margin-bottom: 0; - text-transform: none; - font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -ul.social-buttons { - margin-bottom: 0; -} - -ul.social-buttons li a { - font-size: 20px; - line-height: 40px; - display: block; - width: 40px; - height: 40px; - -webkit-transition: all 0.3s; - -moz-transition: all 0.3s; - transition: all 0.3s; - color: white; - border-radius: 100%; - outline: none; - background-color: #212529; -} - -ul.social-buttons li a:active, ul.social-buttons li a:focus, ul.social-buttons li a:hover { - background-color: #fed136; -} - -.dashboard-card { - background: #ffffff none repeat scroll 0 0; - margin: 15px; - padding: 20px; - border: 0 solid #e7e7e7; - border-radius: 5px; - box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05); -} - -.toolbar-button { - width: 100%; - margin: 5px; -} - -/* --------------------------------------------------- - FEEDBACK STYLE ------------------------------------------------------ */ -.feedback-panel { - list-style-type: none; - padding: 5px; -} - -.feedback-panel .alert { - padding: 5px; - margin-bottom: 5px; - cursor: pointer; -} - -/* --------------------------------------------------- - MEDIAQUERIES ------------------------------------------------------ */ -@media (min-width: 768px) { - section h2.section-heading { - font-size: 3.5vw; - margin-top: 0; - margin-bottom: 15px; - } -} - -@media (min-width: 1500px) { - section h2.section-heading { - font-size: 1.5vw; - margin-top: 0; - margin-bottom: 15px; - } -} - -@media (min-width: 768px) { - .feedback-panel { - position: fixed; - overflow-y: hidden; - max-height: calc(100vh - 60px); - width: 250px; - top: 50px; - right: 15px; - opacity: .8; - z-index: 10000; - } - - .feedback-panel .alert { - padding: 10px; - margin-bottom: 10px; - overflow: hidden; - } - - .feedback-panel .alert:hover { - border-color: #888; - } +body { + overflow-x: hidden; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +p { + line-height: 1.75; +} + +a { + color: #000000; +} + +a:hover { + color: #fec503; +} + +.text-draft { + color: rgba(0, 0, 0, 0.48) !important; +} + +.text-primary { + color: #228bba !important; +} + +.text-warning { + color: #940000 !important; +} + +.text-review { + color: #94028d !important; +} + +.text-success { + color: #007741 !important; +} + +.text-accepted { + color: #fec503 !important; +} + +.text-not-accepted { + color: #A38831 !important; +} + +.text-failed { + color: #A38831 !important; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 700; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section { + padding: 100px 0; +} + +section h2.section-heading { + font-size: 2.5vw; + margin-top: 0; + margin-bottom: 15px; +} + +section h3.section-subheading { + font-size: 16px; + font-weight: 400; + font-style: italic; + margin-bottom: 75px; + text-transform: none; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +@media (min-width: 768px) { + section { + padding: 100px 0; + } +} + +.btn { + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: 700; +} + +.btn-xl { + font-size: 18px; + padding: 20px 40px; +} + +.btn-success { + background-color: #28a745; + border-color: #28a745; +} + +.btn-primary { + background-color: #fed136; + border-color: #fed136; +} + +.btn-default { + background-color: #ffffff; + border-color: #ced4da;; +} + +.btn-info { + background-color: #5bc0de; + border-color: #5bc0de; +} + +.progress-bar { + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #fed136; + transition: width .6s ease; +} + +.btn-primary:active, .btn-primary:focus, .btn-primary:hover { + background-color: #fec810 !important; + border-color: #fec810 !important; + color: white; +} + +.btn-primary:active, .btn-primary:focus { + box-shadow: 0 0 0 0.2rem rgba(254, 209, 55, 0.5) !important; +} + +::-moz-selection { + background: #fed136; + text-shadow: none; +} + +::selection { + background: #fed136; + text-shadow: none; +} + +img::selection { + background: transparent; +} + +img::-moz-selection { + background: transparent; +} + +#mainNav { + background-color: #212529; +} + +#mainNav .navbar-toggler { + font-size: 12px; + right: 0; + padding: 13px; + text-transform: uppercase; + color: white; + border: 0; + background-color: #fed136; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#mainNav .navbar-brand { + color: #fed136; + font-family: 'Kaushan Script', 'Helvetica Neue', Helvetica, Arial, cursive; +} + +#mainNav .navbar-brand.active, #mainNav .navbar-brand:active, #mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover { + color: #fec503; +} + +#mainNav .navbar-nav .nav-item .nav-link { + font-size: 90%; + font-weight: 400; + padding: 0.75em 0; + letter-spacing: 1px; + color: white; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#mainNav .navbar-nav .nav-item .nav-link.active, #mainNav .navbar-nav .nav-item .nav-link:hover { + color: #fed136; +} + +@media (min-width: 992px) { + #mainNav { + padding-top: 25px; + padding-bottom: 25px; + -webkit-transition: padding-top 0.3s, padding-bottom 0.3s; + -moz-transition: padding-top 0.3s, padding-bottom 0.3s; + transition: padding-top 0.3s, padding-bottom 0.3s; + border: none; + background-color: transparent; + } + + #mainNav .navbar-brand { + font-size: 1.75em; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + } + + #mainNav .navbar-nav .nav-item .nav-link { + padding: 1.1em 1em !important; + } + + #mainNav.navbar-shrink { + padding-top: 0; + padding-bottom: 0; + background-color: #212529; + } + + #mainNav.navbar-shrink .navbar-brand { + font-size: 1.25em; + padding: 12px 0; + } +} + +header.masthead { + text-align: center; + color: white; + background-image: url("../img/header-bg.jpg"); + background-repeat: no-repeat; + background-attachment: scroll; + background-position: center center; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +header.masthead .intro-text { + padding-top: 150px; + padding-bottom: 100px; +} + +header.masthead .intro-text .intro-lead-in { + font-size: 22px; + font-style: italic; + line-height: 22px; + margin-bottom: 25px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +header.masthead .intro-text .intro-heading { + font-size: 50px; + font-weight: 700; + line-height: 50px; + margin-bottom: 25px; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +@media (min-width: 768px) { + header.masthead .intro-text { + padding-top: 300px; + padding-bottom: 200px; + } + + header.masthead .intro-text .intro-lead-in { + font-size: 40px; + font-style: italic; + line-height: 40px; + margin-bottom: 25px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; + } + + header.masthead .intro-text .intro-heading { + font-size: 75px; + font-weight: 700; + line-height: 75px; + margin-bottom: 50px; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; + } +} + +.service-heading { + margin: 15px 0; + text-transform: none; +} + +#portfolio .portfolio-item { + right: 0; + margin: 0 0 15px; +} + +#portfolio .portfolio-item .portfolio-link { + position: relative; + display: block; + max-width: 400px; + margin: 0 auto; + cursor: pointer; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover { + position: absolute; + width: 100%; + height: 100%; + -webkit-transition: all ease 0.5s; + -moz-transition: all ease 0.5s; + transition: all ease 0.5s; + opacity: 0; + background: rgba(254, 209, 54, 0.9); +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover { + opacity: 1; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content { + font-size: 20px; + position: absolute; + top: 50%; + width: 100%; + height: 20px; + margin-top: -12px; + text-align: center; + color: white; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i { + margin-top: -12px; +} + +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3, +#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4 { + margin: 0; +} + +#portfolio .portfolio-item .portfolio-caption { + max-width: 400px; + margin: 0 auto; + padding: 25px; + text-align: center; + background-color: #fff; +} + +#portfolio .portfolio-item .portfolio-caption h4 { + margin: 0; + text-transform: none; +} + +#portfolio .portfolio-item .portfolio-caption p { + font-size: 16px; + font-style: italic; + margin: 0; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +#portfolio * { + z-index: 2; +} + +@media (min-width: 767px) { + #portfolio .portfolio-item { + margin: 0 0 30px; + } +} + +.portfolio-modal { + padding-right: 0px !important; +} + +.portfolio-modal .modal-dialog { + margin: 1rem; + max-width: 100vw; +} + +.portfolio-modal .modal-content { + padding: 100px 0; + text-align: center; +} + +.portfolio-modal .modal-content h2 { + font-size: 3em; + margin-bottom: 15px; +} + +.portfolio-modal .modal-content p { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content p.item-intro { + font-size: 16px; + font-style: italic; + margin: 20px 0 30px; + font-family: 'Droid Serif', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.portfolio-modal .modal-content ul.list-inline { + margin-top: 0; + margin-bottom: 30px; +} + +.portfolio-modal .modal-content img { + margin-bottom: 30px; +} + +.portfolio-modal .modal-content button { + cursor: pointer; +} + +.portfolio-modal .close-modal { + position: absolute; + top: 25px; + right: 25px; + width: 75px; + height: 75px; + cursor: pointer; + background-color: transparent; +} + +.portfolio-modal .close-modal:hover { + opacity: 0.3; +} + +.portfolio-modal .close-modal .lr { + /* Safari and Chrome */ + z-index: 1051; + width: 1px; + height: 75px; + margin-left: 35px; + /* IE 9 */ + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + background-color: #212529; +} + +.portfolio-modal .close-modal .lr .rl { + /* Safari and Chrome */ + z-index: 1052; + width: 1px; + height: 75px; + /* IE 9 */ + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + background-color: #212529; +} + +.timeline { + position: relative; + padding: 0; + list-style: none; +} + +.timeline:before { + position: absolute; + top: 0; + bottom: 0; + left: 40px; + width: 2px; + margin-left: -1.5px; + content: ''; + background-color: #e9ecef; +} + +.timeline > li { + position: relative; + min-height: 50px; + margin-bottom: 50px; +} + +.timeline > li:after, .timeline > li:before { + display: table; + content: ' '; +} + +.timeline > li:after { + clear: both; +} + +.timeline > li .timeline-panel { + position: relative; + float: right; + width: 100%; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li .timeline-image { + position: absolute; + z-index: 100; + left: 0; + width: 80px; + height: 80px; + margin-left: 0; + text-align: center; + color: white; + border: 7px solid #e9ecef; + border-radius: 100%; + background-color: #fed136; +} + +.timeline > li .timeline-image h4 { + font-size: 10px; + line-height: 14px; + margin-top: 12px; +} + +.timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 20px 0 100px; + text-align: left; +} + +.timeline > li.timeline-inverted > .timeline-panel:before { + right: auto; + left: -15px; + border-right-width: 15px; + border-left-width: 0; +} + +.timeline > li.timeline-inverted > .timeline-panel:after { + right: auto; + left: -14px; + border-right-width: 14px; + border-left-width: 0; +} + +.timeline > li:last-child { + margin-bottom: 0; +} + +.timeline .timeline-heading h4 { + margin-top: 0; + color: inherit; +} + +.timeline .timeline-heading h4.subheading { + text-transform: none; +} + +.timeline .timeline-body > ul, +.timeline .timeline-body > p { + margin-bottom: 0; +} + +@media (min-width: 768px) { + .timeline:before { + left: 50%; + } + + .timeline > li { + min-height: 100px; + margin-bottom: 100px; + } + + .timeline > li .timeline-panel { + float: left; + width: 41%; + padding: 0 20px 20px 30px; + text-align: right; + } + + .timeline > li .timeline-image { + left: 50%; + width: 100px; + height: 100px; + margin-left: -50px; + } + + .timeline > li .timeline-image h4 { + font-size: 13px; + line-height: 18px; + margin-top: 16px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + float: right; + padding: 0 30px 20px 20px; + text-align: left; + } +} + +@media (min-width: 992px) { + .timeline > li { + min-height: 150px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px; + } + + .timeline > li .timeline-image { + width: 150px; + height: 150px; + margin-left: -75px; + } + + .timeline > li .timeline-image h4 { + font-size: 18px; + line-height: 26px; + margin-top: 30px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 20px 20px; + } +} + +@media (min-width: 1200px) { + .timeline > li { + min-height: 170px; + } + + .timeline > li .timeline-panel { + padding: 0 20px 20px 100px; + } + + .timeline > li .timeline-image { + width: 170px; + height: 170px; + margin-left: -85px; + } + + .timeline > li .timeline-image h4 { + margin-top: 40px; + } + + .timeline > li.timeline-inverted > .timeline-panel { + padding: 0 100px 20px 20px; + } +} + +.team-member { + margin-bottom: 50px; + text-align: center; +} + +.team-member img { + width: 225px; + height: 225px; + border: 7px solid #fff; +} + +.team-member h4 { + margin-top: 25px; + margin-bottom: 0; + text-transform: none; +} + +.team-member p { + margin-top: 0; +} + +section#contact { + background-color: #212529; + background-image: url("../img/map-image.png"); + background-repeat: no-repeat; + background-position: center; +} + +section#contact .section-heading { + color: #fff; +} + +section#contact .form-group { + margin-bottom: 25px; +} + +section#contact .form-group input, +section#contact .form-group textarea { + padding: 20px; +} + +section#contact .form-group input.form-control { + height: auto; +} + +section#contact .form-group textarea.form-control { + height: 248px; +} + +section#contact .form-control:focus { + border-color: #fed136; + box-shadow: none; +} + +section#contact ::-webkit-input-placeholder { + font-weight: 700; + color: #ced4da; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact :-moz-placeholder { + font-weight: 700; + color: #ced4da; + /* Firefox 18- */ + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact ::-moz-placeholder { + font-weight: 700; + color: #ced4da; + /* Firefox 19+ */ + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +section#contact :-ms-input-placeholder { + font-weight: 700; + color: #ced4da; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +footer { + padding: 25px 0; + text-align: center; +} + +footer span.copyright { + font-size: 90%; + line-height: 40px; + text-transform: none; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +footer ul.quicklinks { + font-size: 90%; + line-height: 40px; + margin-bottom: 0; + text-transform: none; + font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +ul.social-buttons { + margin-bottom: 0; +} + +ul.social-buttons li a { + font-size: 20px; + line-height: 40px; + display: block; + width: 40px; + height: 40px; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; + color: white; + border-radius: 100%; + outline: none; + background-color: #212529; +} + +ul.social-buttons li a:active, ul.social-buttons li a:focus, ul.social-buttons li a:hover { + background-color: #fed136; +} + +.dashboard-card { + background: #ffffff none repeat scroll 0 0; + margin: 15px; + padding: 20px; + border: 0 solid #e7e7e7; + border-radius: 5px; + box-shadow: 0 5px 20px rgba(0, 0, 0, 0.05); +} + +.toolbar-button { + width: 100%; + margin: 5px; +} + +/* --------------------------------------------------- + FEEDBACK STYLE +----------------------------------------------------- */ +.feedback-panel { + list-style-type: none; + padding: 5px; +} + +.feedback-panel .alert { + padding: 5px; + margin-bottom: 5px; + cursor: pointer; +} + +/* --------------------------------------------------- + MEDIAQUERIES +----------------------------------------------------- */ +@media (min-width: 768px) { + section h2.section-heading { + font-size: 3.5vw; + margin-top: 0; + margin-bottom: 15px; + } +} + +@media (min-width: 1500px) { + section h2.section-heading { + font-size: 1.5vw; + margin-top: 0; + margin-bottom: 15px; + } +} + +@media (min-width: 768px) { + .feedback-panel { + position: fixed; + overflow-y: hidden; + max-height: calc(100vh - 60px); + width: 250px; + top: 50px; + right: 15px; + opacity: .8; + z-index: 10000; + } + + .feedback-panel .alert { + padding: 10px; + margin-bottom: 10px; + overflow: hidden; + } + + .feedback-panel .alert:hover { + border-color: #888; + } } \ No newline at end of file diff --git a/src/main/resources/public/css/conference.css b/src/main/resources/public/css/conference.css index ec88796..8cebd1d 100644 --- a/src/main/resources/public/css/conference.css +++ b/src/main/resources/public/css/conference.css @@ -2,7 +2,22 @@ body { min-width: 400px; } +.conference-row .col:hover { + background-color: #f3f3f3; + border-radius: .25rem; +} + +.filter-option-inner-inner { + color: white; +} + +.filter .dropdown { + margin-bottom: 10px; +} +.conference-row .col .text-decoration { + text-decoration: none; +} .form-group textarea { @@ -19,6 +34,7 @@ body { .deadline { margin: 0; height: 40px; + min-height: 40px; } .deadline-text { @@ -33,8 +49,46 @@ body { .member { margin: 0; + height: 40px; + max-height: 40px; +} + +.member select { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + border: none; + outline: none; + padding: 0.5rem 1.75em 0.5rem 0.5em; + display: inline-block; + background: transparent url("https://cdn3.iconfinder.com/data/icons/faticons/32/arrow-down-01-16.png") no-repeat right 7px center; + } +.member select:nth-child(4) { + border-right: 1px solid #ced4da; +} + + +.member-name { + padding: .75rem 1.25rem; + cursor: default; + outline: none; + border: none; + border-right: 1px solid #ced4da; +} + +#take-part[disabled=disabled] { + background-color: #ced4da; + border-color: #c2c5c7; +} + +#take-part[disabled=disabled]:hover { + background-color: #737475 !important; + border-color: #5d5e5f !important; +} + + .paper-list { height: 200px; padding: 0px; @@ -43,6 +97,8 @@ body { .paper { margin: 0; + min-height: 40px; + height: 40px; } .paper-name { @@ -63,6 +119,9 @@ body { .icon-delete { background-color: #f44; + background-image: url(/img/conference/delete.png); + background-repeat: round; + color: transparent !important; } .icon-delete:hover { @@ -95,13 +154,14 @@ body { float: right; } -@media (max-width: 1199px) { +@media (max-width: 1199px) and (min-width: 768px) { .paper-control { display: block!important; } } @media (max-width: 991px) { + .dates-panel { display: block!important; } diff --git a/src/main/resources/public/css/grant.css b/src/main/resources/public/css/grant.css new file mode 100644 index 0000000..1073cdf --- /dev/null +++ b/src/main/resources/public/css/grant.css @@ -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; +} \ No newline at end of file diff --git a/src/main/resources/public/css/paper.css b/src/main/resources/public/css/paper.css index 6e61d0c..824209f 100644 --- a/src/main/resources/public/css/paper.css +++ b/src/main/resources/public/css/paper.css @@ -1,5 +1,9 @@ -#files-list .row > div:nth-child(6) { +#files-list .row > div:nth-child(7) { display: flex; justify-content: center; flex-direction: column; +} + +.nav-tabs { + margin-bottom: 20px; } \ No newline at end of file diff --git a/src/main/resources/public/css/tasks.css b/src/main/resources/public/css/tasks.css index 2f42db5..14718de 100644 --- a/src/main/resources/public/css/tasks.css +++ b/src/main/resources/public/css/tasks.css @@ -1,24 +1,65 @@ -.bootstrap-tagsinput{ - +.tags-container { width: 100%; padding: .375rem .75rem; + background-color: #fff; + border: 1px solid #ccc; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + display: inline-block; + color: #555; + vertical-align: middle; + border-radius: 4px; + max-width: 100%; + line-height: 22px; + cursor: text; +} + +.input-tag-name { + border: none; + box-shadow: none; + outline: none; + background-color: transparent; + padding: 0 6px; + margin: 0; + width: auto; + max-width: inherit; } -.bootstrap-tagsinput .label{ +.tag { + display: inline-block; + padding: .2em .6em .3em; + background-color: orange; + border-radius: .25em; + margin-right: 4px; + margin-bottom: 4px; + + font-size: 75%; + font-weight: 700; + line-height: 1.5; + color: #fff; +} + +.tag-name span[data-role="remove"] { + margin-left: 8px; + cursor: pointer; +} - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: 700; - line-height: 2.5; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; +.tag-name span[data-role="remove"]:after { + content: "x"; + padding: 0px 2px; } -.bootstrap-tagsinput .label-info{ +.tag-name input[type="text"] { + background: transparent; + border: none; + display: inline-flex; + font-size: 100%; + font-weight: 700; + line-height: 1.5; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + outline: none; + cursor: default; - background-color: orange; } diff --git a/src/main/resources/public/js/conference.js b/src/main/resources/public/js/conference.js index 304daae..8335f54 100644 --- a/src/main/resources/public/js/conference.js +++ b/src/main/resources/public/js/conference.js @@ -1,27 +1,29 @@ $(document).ready(function () { - $('.data-href-js').click( function() { - window.location = $(this).attr('data-href'); - }); - - $('.circle').parent().click( function() { - $(this).children('.circle').toggleClass('circle-active'); - }); - - $('.checkbox-js').parent().click( function() { - $(this).children('.checkbox').toggleClass('selected'); - }); - - $('#select-all-js').click( function() { - $(this).toggleClass('selected'); + $('a[data-confirm]').click(function (ev) { + var href = $(this).attr('href'); + if (!$('#dataConfirmModal').length) { + $('#modalDelete').append(''); + } + $('#dataConfirmModal').find('#myModalLabel').text($(this).attr('data-confirm')); + $('#dataConfirmOK').attr('href', href); + $('#dataConfirmModal').modal({show: true}); + return false; }); }); diff --git a/src/main/resources/public/js/core.js b/src/main/resources/public/js/core.js index bf6fb77..58e68f4 100644 --- a/src/main/resources/public/js/core.js +++ b/src/main/resources/public/js/core.js @@ -3,6 +3,7 @@ var urlFileUpload = "/api/1.0/files/uploadTmpFile"; var urlFileDownload = "/api/1.0/files/download/"; +var urlPdfGenerating = "/papers/generatePdf"; var urlFileDownloadTmp = "/api/1.0/files/download-tmp/"; /* exported MessageTypesEnum */ diff --git a/src/main/resources/public/js/tasks.js b/src/main/resources/public/js/tasks.js index 6bf160c..6ee3537 100644 --- a/src/main/resources/public/js/tasks.js +++ b/src/main/resources/public/js/tasks.js @@ -1,5 +1,75 @@ /*") + .attr("id", 'tags' + tagNumber) + .addClass("tag"); + // контейнер id + var idInput = $("") + .attr("type", "hidden") + .attr("id", "tags" + tagNumber + ".id") + .attr("name", "tags[" + tagNumber + "].id") + .attr("value", ''); + // контейнер текста + var conDiv = $("
") + .addClass("tag-name"); + // текст тега + var nameInput = $("") + .attr("type", "text") + .attr("id", "tags" + tagNumber + ".tagName") + .attr("name", "tags[" + tagNumber + "].tagName") + .attr("value", tagName) + .attr("readonly", "true") + .attr("size", tagName.length); + // кнопка удалить тег + var removeSpan = $("") + .attr("data-role", "remove") + .bind("click", removeTag); + + conDiv.append(nameInput); + conDiv.append(removeSpan); + newTagRow.append(idInput); + newTagRow.append(conDiv); + $(this).before(newTagRow); + } + + $(this).val(''); + } + }); + + $("span[data-role=remove]").click(removeTag); $(".task-row").mouseenter(function (event) { var taskRow = $(event.target).closest(".task-row"); $(taskRow).css("background-color", "#f8f9fa"); diff --git a/src/main/resources/templates/conferences/conference.html b/src/main/resources/templates/conferences/conference.html index bcb0e7b..72c6cea 100644 --- a/src/main/resources/templates/conferences/conference.html +++ b/src/main/resources/templates/conferences/conference.html @@ -18,47 +18,59 @@
-
+
- +
- +

Incorrect title

+

- +
-
- -
+ +
+ - - Удалить + placeholder="Описание" + th:field="*{deadlines[__${rowStat.index}__].description}"/> + +
-
+

Incorrect title

+

+
-
@@ -70,11 +82,13 @@
- +
- +
@@ -83,60 +97,64 @@
-
-
- Пользователь 1 -
-
- очная -
-
- статья -
-
- -
-
- Пользователь 1 -
-
- очная -
-
- доклад -
+
+ + + + +
-
- + +
- + +
diff --git a/src/main/resources/templates/conferences/fragments/confLineFragment.html b/src/main/resources/templates/conferences/fragments/confLineFragment.html index f54a777..7d948d4 100644 --- a/src/main/resources/templates/conferences/fragments/confLineFragment.html +++ b/src/main/resources/templates/conferences/fragments/confLineFragment.html @@ -4,15 +4,15 @@ -
-
- - - +
+ diff --git a/src/main/resources/templates/conferences/fragments/confNavigationFragment.html b/src/main/resources/templates/conferences/fragments/confNavigationFragment.html index 31cc00f..7e67365 100644 --- a/src/main/resources/templates/conferences/fragments/confNavigationFragment.html +++ b/src/main/resources/templates/conferences/fragments/confNavigationFragment.html @@ -13,7 +13,7 @@
@@ -21,7 +21,7 @@
- Добавить конференцию + Новая конференцию
diff --git a/src/main/resources/templates/grants/fragments/grantDashboardFragment.html b/src/main/resources/templates/grants/fragments/grantDashboardFragment.html index df3e3d0..51d0925 100644 --- a/src/main/resources/templates/grants/fragments/grantDashboardFragment.html +++ b/src/main/resources/templates/grants/fragments/grantDashboardFragment.html @@ -10,7 +10,9 @@
- title + + title +

status

diff --git a/src/main/resources/templates/grants/fragments/grantLineFragment.html b/src/main/resources/templates/grants/fragments/grantLineFragment.html index 1048897..dda0e8d 100644 --- a/src/main/resources/templates/grants/fragments/grantLineFragment.html +++ b/src/main/resources/templates/grants/fragments/grantLineFragment.html @@ -9,7 +9,7 @@ - + - +
diff --git a/src/main/resources/templates/grants/grant.html b/src/main/resources/templates/grants/grant.html index fe4d755..832ed56 100644 --- a/src/main/resources/templates/grants/grant.html +++ b/src/main/resources/templates/grants/grant.html @@ -3,13 +3,12 @@ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html"> - +
- -
+
@@ -17,6 +16,7 @@
+
Incorrect title

-
-
- +
-
+
@@ -71,31 +70,101 @@ class="alert alert-danger">Incorrect title

- +
-
+
+