Merge remote-tracking branch 'remotes/origin/dev' into 69-take-part-conf
# Conflicts: # src/main/resources/db/changelog-master.xml
This commit is contained in:
commit
713e50043e
@ -22,7 +22,7 @@ checkRun:
|
|||||||
|
|
||||||
checkStyle:
|
checkStyle:
|
||||||
stage: test
|
stage: test
|
||||||
script: ./gradlew check
|
script: ./gradlew check -x test
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
|
@ -1,50 +1,46 @@
|
|||||||
## Краткое описание задачи
|
## Краткое описание задачи
|
||||||
```
|
|
||||||
Что требуется сделать
|
Что требуется сделать
|
||||||
```
|
|
||||||
|
|
||||||
## `Опционально` Список верстаемых страниц
|
## `Опционально` Список верстаемых страниц
|
||||||
```
|
|
||||||
Будут затронуты страницы:
|
Будут затронуты страницы:
|
||||||
* page1.html
|
* page1.html
|
||||||
* page2.html
|
* page2.html
|
||||||
* page3.html
|
* page3.html
|
||||||
```
|
|
||||||
|
## `Опционально` Список затрагиваемых модулей
|
||||||
## `Опционально` Список затрагиваемых модулей
|
|
||||||
```
|
При реализации задачи потребуется также реализовать методы контроллера
|
||||||
При реализации задачи потребуется также реализовать методы контроллера
|
|
||||||
```
|
|
||||||
|
## `Опционально` Список реализуемых функций
|
||||||
## `Опционально` Список реализуемых функций
|
|
||||||
```
|
После выполнения задачи станет доступным:
|
||||||
После выполнения задачи станет доступным:
|
* просмотр `entity_name`
|
||||||
* просмотр `entity_name`
|
* редактирование `entity_name`
|
||||||
* редактирование `entity_name`
|
* валидация `entity_name`
|
||||||
* валидация `entity_name`
|
|
||||||
```
|
## `Опционально` Сценарии работы
|
||||||
|
|
||||||
## `Опционально` Сценарии работы
|
Сценарий просмотра:
|
||||||
```
|
1. Зайти на главную страницу приложения
|
||||||
Сценарий просмотра:
|
2. Перейти в раздел `section_name`
|
||||||
1. Зайти на главную страницу приложения
|
3. Перейти к списку `entity_name`
|
||||||
2. Перейти в раздел `section_name`
|
4. Выбрать нужную `entity_name` и нажать на нее
|
||||||
3. Перейти к списку `entity_name`
|
|
||||||
4. Выбрать нужную `entity_name` и нажать на нее
|
Сценарий редактирования:
|
||||||
|
1. Зайти на главную страницу приложения
|
||||||
Сценарий редактирования:
|
2. Перейти в раздел `section_name`
|
||||||
1. Зайти на главную страницу приложения
|
3. Перейти к списку `entity_name`
|
||||||
2. Перейти в раздел `section_name`
|
4. Выбрать нужную `entity_name` и нажать на нее
|
||||||
3. Перейти к списку `entity_name`
|
5. Внести нужные правки в поля и сохранить
|
||||||
4. Выбрать нужную `entity_name` и нажать на нее
|
|
||||||
5. Внести нужные правки в поля и сохранить
|
## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы
|
||||||
```
|
|
||||||
|
* Сверстаны страницы page1.hml, page2.hml, page3.hml
|
||||||
## Описание конечного результата, дающего возможность проверки выполнения задачи: компоненты проекта, сценарии работы
|
* Реализован контроллер для обслуживания страниц
|
||||||
```
|
* Сохранение в БД еще не реализовано
|
||||||
* Сверстаны страницы page1.hml, page2.hml, page3.hml
|
* Валидация происходит по полям `field1, field2`
|
||||||
* Реализован контроллер для обслуживания страниц
|
* Сценарий просмотра проверяется при ручном внечении записей в БД
|
||||||
* Сохранение в БД еще не реализовано
|
|
||||||
* Валидация происходит по полям `field1, field2`
|
|
||||||
* Сценарий просмотра проверяется при ручном внечении записей в БД
|
|
||||||
```
|
|
||||||
|
@ -114,9 +114,6 @@ dependencies {
|
|||||||
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3'
|
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3'
|
||||||
compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0'
|
compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0'
|
||||||
|
|
||||||
compile group: 'org.apache.poi', name: 'poi', version: '3.9'
|
|
||||||
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.9'
|
|
||||||
|
|
||||||
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
|
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
|
||||||
|
|
||||||
compile group: 'org.webjars', name: 'bootstrap', version: '4.1.0'
|
compile group: 'org.webjars', name: 'bootstrap', version: '4.1.0'
|
||||||
@ -125,8 +122,10 @@ dependencies {
|
|||||||
compile group: 'org.webjars.npm', name: 'jquery.easing', version: '1.4.1'
|
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: '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-swagger2', version: '2.6.0'
|
||||||
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.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.springframework.boot', name: 'spring-boot-starter-test'
|
||||||
|
testCompile group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.3.1'
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
package ru.ulstu.core.error;
|
|
||||||
|
|
||||||
public class XlsLoadException extends Exception {
|
|
||||||
public XlsLoadException(String s) {
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package ru.ulstu.core.error;
|
|
||||||
|
|
||||||
public class XlsParseException extends Exception {
|
|
||||||
public XlsParseException(String s) {
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<T extends TreeEntity> {
|
|
||||||
public TreeDto getTree(String rootName, List<T> rootItems) {
|
|
||||||
return addChildNode(new TreeDto(rootName), rootItems, element -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TreeDto getTree(String rootName, List<T> rootItems, Predicate<T> filterPredicate) {
|
|
||||||
return addChildNode(new TreeDto(rootName), rootItems, filterPredicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeDto addChildNode(TreeDto currentRoot, List<T> children, Predicate<T> 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,207 +0,0 @@
|
|||||||
package ru.ulstu.core.service;
|
|
||||||
|
|
||||||
import org.apache.poi.POIXMLDocument;
|
|
||||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.ss.usermodel.*;
|
|
||||||
import org.apache.poi.ss.util.CellRangeAddress;
|
|
||||||
import org.apache.poi.ss.util.RegionUtil;
|
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
||||||
import ru.ulstu.core.error.XlsParseException;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
public class XlsDocumentBuilder {
|
|
||||||
private static final int DEFAULT_SHEET_NUM = 0;
|
|
||||||
private File documentFile;
|
|
||||||
private Workbook workBook;
|
|
||||||
private Sheet currentSheet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for reading and writing data from/to *.[xls|xlsx] document
|
|
||||||
*
|
|
||||||
* @param file contains existing document for reading or new document to save
|
|
||||||
*/
|
|
||||||
public XlsDocumentBuilder(File file) throws IOException, XlsParseException {
|
|
||||||
this.documentFile = file;
|
|
||||||
if (file.exists()) {
|
|
||||||
workBook = getWorkBook(file);
|
|
||||||
currentSheet = workBook.getSheetAt(DEFAULT_SHEET_NUM);
|
|
||||||
} else {
|
|
||||||
workBook = new XSSFWorkbook();
|
|
||||||
currentSheet = workBook.createSheet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Workbook getWorkBook(File file) throws XlsParseException, IOException {
|
|
||||||
InputStream inputStream = new PushbackInputStream(new FileInputStream(file), 4096);
|
|
||||||
if (isXlsx(inputStream)) {
|
|
||||||
return new XSSFWorkbook(inputStream);
|
|
||||||
} else if (isExcel(inputStream)) {
|
|
||||||
return new HSSFWorkbook(inputStream);
|
|
||||||
}
|
|
||||||
throw new XlsParseException("Wrong document format");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change active sheet to write or read data
|
|
||||||
*
|
|
||||||
* @param index index of sheet to activate
|
|
||||||
*/
|
|
||||||
public XlsDocumentBuilder setActiveSheet(int index) {
|
|
||||||
workBook.setActiveSheet(index);
|
|
||||||
currentSheet = workBook.getSheetAt(index);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new sheet in document and set it active
|
|
||||||
*
|
|
||||||
* @param sheetName
|
|
||||||
*/
|
|
||||||
public XlsDocumentBuilder insertNewSheet(String sheetName) {
|
|
||||||
currentSheet = workBook.createSheet(sheetName);
|
|
||||||
workBook.setActiveSheet(getSheetCount() - 1);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder insertNewSheet(String sheetName, int order) {
|
|
||||||
insertNewSheet(sheetName);
|
|
||||||
workBook.setSheetOrder(sheetName, order);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns number of sheet in document
|
|
||||||
*
|
|
||||||
* @return sheets count
|
|
||||||
*/
|
|
||||||
public int getSheetCount() {
|
|
||||||
return workBook.getNumberOfSheets();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns number of rows in sheet
|
|
||||||
*
|
|
||||||
* @return rows count
|
|
||||||
*/
|
|
||||||
public int getRowCount() {
|
|
||||||
return currentSheet.getLastRowNum();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns number of columns in sheet
|
|
||||||
*
|
|
||||||
* @return columns count
|
|
||||||
*/
|
|
||||||
public int getColumnCount() {
|
|
||||||
Row row = currentSheet.getRow(getRowCount());
|
|
||||||
if (row == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return row.getLastCellNum() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns converted to string representation of cell value
|
|
||||||
*
|
|
||||||
* @param rowIndex row index of current sheet
|
|
||||||
* @param colIndex column index of current sheet
|
|
||||||
* @return string value of cell
|
|
||||||
*/
|
|
||||||
public String getCellAsString(int rowIndex, int colIndex) {
|
|
||||||
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
|
||||||
cell.setCellType(Cell.CELL_TYPE_STRING);
|
|
||||||
return cell.getStringCellValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets string cell value
|
|
||||||
*
|
|
||||||
* @param rowIndex row index of current sheet
|
|
||||||
* @param colIndex column index of current sheet
|
|
||||||
*/
|
|
||||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, String value) {
|
|
||||||
if (currentSheet.getRow(rowIndex) == null) {
|
|
||||||
currentSheet.createRow(rowIndex);
|
|
||||||
}
|
|
||||||
if (currentSheet.getRow(rowIndex).getCell(colIndex) == null) {
|
|
||||||
currentSheet.getRow(rowIndex).createCell(colIndex);
|
|
||||||
}
|
|
||||||
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
|
||||||
cell.setCellValue(value);
|
|
||||||
setColumnAutosize(colIndex, colIndex);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, int value) {
|
|
||||||
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, long value) {
|
|
||||||
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save current file
|
|
||||||
*/
|
|
||||||
public XlsDocumentBuilder save() throws IOException {
|
|
||||||
OutputStream out = new FileOutputStream(documentFile);
|
|
||||||
workBook.write(out);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isExcel(InputStream i) throws IOException {
|
|
||||||
return (POIFSFileSystem.hasPOIFSHeader(i) || POIXMLDocument.hasOOXMLHeader(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isXlsx(InputStream i) throws IOException {
|
|
||||||
return POIXMLDocument.hasOOXMLHeader(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getActiveSheetIndex() {
|
|
||||||
return workBook.getActiveSheetIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder mergeCells(int rowFrom, int rowTo, int colFrom, int colTo) {
|
|
||||||
currentSheet.addMergedRegion(new CellRangeAddress(rowFrom, rowTo, colFrom, colTo));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegionBorderWithMedium(int rowFrom, int rowTo, int colFrom, int colTo) {
|
|
||||||
for (int row = rowFrom; row < rowTo; row++) {
|
|
||||||
for (int col = colFrom; col <= colTo; col++) {
|
|
||||||
CellRangeAddress cellRangeAddress = new CellRangeAddress(row, row, col, col);
|
|
||||||
RegionUtil.setBorderBottom(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
|
||||||
RegionUtil.setBorderLeft(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
|
||||||
RegionUtil.setBorderRight(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
|
||||||
RegionUtil.setBorderTop(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder setColumnAutosize(int from, int to) {
|
|
||||||
for (int col = from; col <= to; col++) {
|
|
||||||
currentSheet.autoSizeColumn(col, true);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder setRowAutosize(int from, int to) {
|
|
||||||
CellStyle style = workBook.createCellStyle();
|
|
||||||
style.setWrapText(true);
|
|
||||||
for (int row = from; row <= to; row++) {
|
|
||||||
for (int col = 0; col <= currentSheet.getRow(row).getLastCellNum(); col++) {
|
|
||||||
if (currentSheet.getRow(row).getCell(col) != null) {
|
|
||||||
currentSheet.getRow(row).getCell(col).setCellStyle(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XlsDocumentBuilder deleteSheet(int index) {
|
|
||||||
workBook.removeSheetAt(index);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,6 +19,9 @@ public class FileData extends BaseEntity {
|
|||||||
|
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
|
@Column(name = "is_latex_attach")
|
||||||
|
private boolean isLatexAttach;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -50,4 +53,12 @@ public class FileData extends BaseEntity {
|
|||||||
public void setData(byte[] data) {
|
public void setData(byte[] data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLatexAttach() {
|
||||||
|
return isLatexAttach;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLatexAttach(boolean latexAttach) {
|
||||||
|
isLatexAttach = latexAttach;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ public class FileDataDto {
|
|||||||
private String fileName;
|
private String fileName;
|
||||||
private String tmpFileName;
|
private String tmpFileName;
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
private boolean isLatexAttach;
|
||||||
|
|
||||||
public FileDataDto() {
|
public FileDataDto() {
|
||||||
}
|
}
|
||||||
@ -16,17 +17,20 @@ public class FileDataDto {
|
|||||||
@JsonCreator
|
@JsonCreator
|
||||||
public FileDataDto(@JsonProperty("id") Integer id,
|
public FileDataDto(@JsonProperty("id") Integer id,
|
||||||
@JsonProperty("name") String name,
|
@JsonProperty("name") String name,
|
||||||
|
@JsonProperty("isLatexAttach") boolean isLatexAttach,
|
||||||
@JsonProperty("fileName") String fileName,
|
@JsonProperty("fileName") String fileName,
|
||||||
@JsonProperty("tmpFileName") String tmpFileName) {
|
@JsonProperty("tmpFileName") String tmpFileName) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.tmpFileName = tmpFileName;
|
this.tmpFileName = tmpFileName;
|
||||||
|
this.isLatexAttach = isLatexAttach;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDataDto(FileData fileData) {
|
public FileDataDto(FileData fileData) {
|
||||||
this.id = fileData.getId();
|
this.id = fileData.getId();
|
||||||
this.name = fileData.getName();
|
this.name = fileData.getName();
|
||||||
|
this.isLatexAttach = fileData.isLatexAttach();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDataDto(String fileName, String tmpFileName) {
|
public FileDataDto(String fileName, String tmpFileName) {
|
||||||
@ -73,4 +77,19 @@ public class FileDataDto {
|
|||||||
this.deleted = deleted;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import ru.ulstu.file.model.FileData;
|
import ru.ulstu.file.model.FileData;
|
||||||
import ru.ulstu.file.model.FileDataDto;
|
import ru.ulstu.file.model.FileDataDto;
|
||||||
import ru.ulstu.file.repostory.FileRepository;
|
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.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -15,6 +18,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
@ -107,6 +111,7 @@ public class FileService {
|
|||||||
|
|
||||||
private FileData copyFromDto(FileData fileData, FileDataDto fileDataDto) {
|
private FileData copyFromDto(FileData fileData, FileDataDto fileDataDto) {
|
||||||
fileData.setName(fileDataDto.getName());
|
fileData.setName(fileDataDto.getName());
|
||||||
|
fileData.setLatexAttach(fileDataDto.isLatexAttach());
|
||||||
return fileData;
|
return fileData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,4 +123,27 @@ public class FileService {
|
|||||||
public FileDataDto createFromMultipartFile(MultipartFile multipartFile) throws IOException {
|
public FileDataDto createFromMultipartFile(MultipartFile multipartFile) throws IOException {
|
||||||
return new FileDataDto(multipartFile.getOriginalFilename(), uploadToTmpDir(multipartFile));
|
return new FileDataDto(multipartFile.getOriginalFilename(), uploadToTmpDir(multipartFile));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void createLatexAttachs(PaperDto paper) throws IOException {
|
||||||
|
for (FileDataDto fileDataDto : paper.getFiles()
|
||||||
|
.stream()
|
||||||
|
.filter(f -> 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.findOne(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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import ru.ulstu.deadline.model.Deadline;
|
|||||||
import ru.ulstu.grant.model.Grant;
|
import ru.ulstu.grant.model.Grant;
|
||||||
import ru.ulstu.grant.model.GrantDto;
|
import ru.ulstu.grant.model.GrantDto;
|
||||||
import ru.ulstu.grant.service.GrantService;
|
import ru.ulstu.grant.service.GrantService;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
import springfox.documentation.annotations.ApiIgnore;
|
import springfox.documentation.annotations.ApiIgnore;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
@ -24,7 +25,6 @@ import static org.springframework.util.StringUtils.isEmpty;
|
|||||||
import static ru.ulstu.core.controller.Navigation.GRANTS_PAGE;
|
import static ru.ulstu.core.controller.Navigation.GRANTS_PAGE;
|
||||||
import static ru.ulstu.core.controller.Navigation.GRANT_PAGE;
|
import static ru.ulstu.core.controller.Navigation.GRANT_PAGE;
|
||||||
import static ru.ulstu.core.controller.Navigation.REDIRECT_TO;
|
import static ru.ulstu.core.controller.Navigation.REDIRECT_TO;
|
||||||
import static ru.ulstu.core.controller.Navigation.hasErrors;
|
|
||||||
|
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
@ -57,27 +57,42 @@ public class GrantController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/grant", params = "save")
|
@PostMapping(value = "/grant", params = "save")
|
||||||
public String save(@Valid GrantDto grantDto, Errors errors) throws IOException {
|
public String save(@Valid GrantDto grantDto, Errors errors)
|
||||||
|
throws IOException {
|
||||||
filterEmptyDeadlines(grantDto);
|
filterEmptyDeadlines(grantDto);
|
||||||
if (grantDto.getDeadlines().isEmpty()) {
|
if (grantDto.getDeadlines().isEmpty()) {
|
||||||
errors.rejectValue("deadlines", "errorCode", "Не может быть пустым");
|
errors.rejectValue("deadlines", "errorCode", "Не может быть пусто");
|
||||||
|
}
|
||||||
|
if (grantDto.getLeaderId().equals(-1)) {
|
||||||
|
errors.rejectValue("leaderId", "errorCode", "Укажите руководителя");
|
||||||
|
}
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
return GRANT_PAGE;
|
||||||
}
|
}
|
||||||
hasErrors(errors, GRANT_PAGE);
|
|
||||||
grantService.save(grantDto);
|
grantService.save(grantDto);
|
||||||
return String.format(REDIRECT_TO, GRANTS_PAGE);
|
return String.format(REDIRECT_TO, GRANTS_PAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/grant", params = "filterUsers")
|
||||||
|
public String filterUsers() {
|
||||||
|
return GRANT_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/grant", params = "addDeadline")
|
@PostMapping(value = "/grant", params = "addDeadline")
|
||||||
public String addDeadline(@Valid GrantDto grantDto, Errors errors) {
|
public String addDeadline(@Valid GrantDto grantDto, Errors errors) {
|
||||||
filterEmptyDeadlines(grantDto);
|
filterEmptyDeadlines(grantDto);
|
||||||
hasErrors(errors, GRANT_PAGE);
|
if (errors.hasErrors()) {
|
||||||
|
return GRANT_PAGE;
|
||||||
|
}
|
||||||
grantDto.getDeadlines().add(new Deadline());
|
grantDto.getDeadlines().add(new Deadline());
|
||||||
return GRANT_PAGE;
|
return GRANT_PAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/grant", params = "createProject")
|
@PostMapping(value = "/grant", params = "createProject")
|
||||||
public String createProject(@Valid GrantDto grantDto, Errors errors) {
|
public String createProject(@Valid GrantDto grantDto, Errors errors) {
|
||||||
hasErrors(errors, GRANT_PAGE);
|
if (errors.hasErrors()) {
|
||||||
|
return GRANT_PAGE;
|
||||||
|
}
|
||||||
grantService.createProject(grantDto);
|
grantService.createProject(grantDto);
|
||||||
return GRANT_PAGE;
|
return GRANT_PAGE;
|
||||||
}
|
}
|
||||||
@ -93,6 +108,11 @@ public class GrantController {
|
|||||||
return grantService.getGrantStatuses();
|
return grantService.getGrantStatuses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("allAuthors")
|
||||||
|
public List<User> getAllAuthors(GrantDto grantDto) {
|
||||||
|
return grantService.getGrantAuthors(grantDto);
|
||||||
|
}
|
||||||
|
|
||||||
private void filterEmptyDeadlines(GrantDto grantDto) {
|
private void filterEmptyDeadlines(GrantDto grantDto) {
|
||||||
grantDto.setDeadlines(grantDto.getDeadlines().stream()
|
grantDto.setDeadlines(grantDto.getDeadlines().stream()
|
||||||
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
|
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
|
||||||
|
@ -2,28 +2,35 @@ package ru.ulstu.grant.model;
|
|||||||
|
|
||||||
import org.hibernate.validator.constraints.NotBlank;
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
import ru.ulstu.core.model.BaseEntity;
|
import ru.ulstu.core.model.BaseEntity;
|
||||||
|
import ru.ulstu.core.model.UserContainer;
|
||||||
import ru.ulstu.deadline.model.Deadline;
|
import ru.ulstu.deadline.model.Deadline;
|
||||||
import ru.ulstu.file.model.FileData;
|
import ru.ulstu.file.model.FileData;
|
||||||
import ru.ulstu.project.model.Project;
|
import ru.ulstu.project.model.Project;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OrderBy;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "grants")
|
@Table(name = "grants")
|
||||||
public class Grant extends BaseEntity {
|
public class Grant extends BaseEntity implements UserContainer {
|
||||||
public enum GrantStatus {
|
public enum GrantStatus {
|
||||||
APPLICATION("Заявка"),
|
APPLICATION("Заявка"),
|
||||||
ON_COMPETITION("Отправлен на конкурс"),
|
ON_COMPETITION("Отправлен на конкурс"),
|
||||||
@ -52,6 +59,7 @@ public class Grant extends BaseEntity {
|
|||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL)
|
@OneToMany(cascade = CascadeType.ALL)
|
||||||
@JoinColumn(name = "grant_id")
|
@JoinColumn(name = "grant_id")
|
||||||
|
@OrderBy("date")
|
||||||
private List<Deadline> deadlines = new ArrayList<>();
|
private List<Deadline> deadlines = new ArrayList<>();
|
||||||
|
|
||||||
//Описание гранта
|
//Описание гранта
|
||||||
@ -67,6 +75,14 @@ public class Grant extends BaseEntity {
|
|||||||
@JoinColumn(name = "project_id")
|
@JoinColumn(name = "project_id")
|
||||||
private Project project;
|
private Project project;
|
||||||
|
|
||||||
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
|
private Set<User> authors = new HashSet<>();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "leader_id")
|
||||||
|
private User leader;
|
||||||
|
|
||||||
public GrantStatus getStatus() {
|
public GrantStatus getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -115,6 +131,27 @@ public class Grant extends BaseEntity {
|
|||||||
this.project = project;
|
this.project = project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<User> getAuthors() {
|
||||||
|
return authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthors(Set<User> authors) {
|
||||||
|
this.authors = authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<User> getUsers() {
|
||||||
|
return getAuthors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getLeader() {
|
||||||
|
return leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeader(User leader) {
|
||||||
|
this.leader = leader;
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<Deadline> getNextDeadline() {
|
public Optional<Deadline> getNextDeadline() {
|
||||||
return deadlines
|
return deadlines
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -2,14 +2,22 @@ package ru.ulstu.grant.model;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
import ru.ulstu.deadline.model.Deadline;
|
import ru.ulstu.deadline.model.Deadline;
|
||||||
import ru.ulstu.project.model.ProjectDto;
|
import ru.ulstu.project.model.ProjectDto;
|
||||||
|
import ru.ulstu.user.model.UserDto;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
||||||
|
|
||||||
public class GrantDto {
|
public class GrantDto {
|
||||||
|
private final static int MAX_AUTHORS_LENGTH = 60;
|
||||||
|
|
||||||
private Integer id;
|
private Integer id;
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String title;
|
private String title;
|
||||||
@ -18,6 +26,12 @@ public class GrantDto {
|
|||||||
private String comment;
|
private String comment;
|
||||||
private String applicationFileName;
|
private String applicationFileName;
|
||||||
private ProjectDto project;
|
private ProjectDto project;
|
||||||
|
private Set<Integer> authorIds;
|
||||||
|
private Set<UserDto> authors;
|
||||||
|
private Integer leaderId;
|
||||||
|
private boolean wasLeader;
|
||||||
|
private boolean hasAge;
|
||||||
|
private boolean hasDegree;
|
||||||
|
|
||||||
public GrantDto() {
|
public GrantDto() {
|
||||||
deadlines.add(new Deadline());
|
deadlines.add(new Deadline());
|
||||||
@ -29,7 +43,13 @@ public class GrantDto {
|
|||||||
@JsonProperty("status") Grant.GrantStatus status,
|
@JsonProperty("status") Grant.GrantStatus status,
|
||||||
@JsonProperty("deadlines") List<Deadline> deadlines,
|
@JsonProperty("deadlines") List<Deadline> deadlines,
|
||||||
@JsonProperty("comment") String comment,
|
@JsonProperty("comment") String comment,
|
||||||
@JsonProperty("project") ProjectDto project) {
|
@JsonProperty("project") ProjectDto project,
|
||||||
|
@JsonProperty("authorIds") Set<Integer> authorIds,
|
||||||
|
@JsonProperty("authors") Set<UserDto> authors,
|
||||||
|
@JsonProperty("leader") Integer leaderId,
|
||||||
|
@JsonProperty("wasLeader") boolean wasLeader,
|
||||||
|
@JsonProperty("hasAge") boolean hasAge,
|
||||||
|
@JsonProperty("hasDegree") boolean hasDegree) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
@ -37,6 +57,11 @@ public class GrantDto {
|
|||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
this.applicationFileName = null;
|
this.applicationFileName = null;
|
||||||
this.project = project;
|
this.project = project;
|
||||||
|
this.authors = authors;
|
||||||
|
this.leaderId = leaderId;
|
||||||
|
this.wasLeader = wasLeader;
|
||||||
|
this.hasAge = hasAge;
|
||||||
|
this.hasDegree = hasDegree;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GrantDto(Grant grant) {
|
public GrantDto(Grant grant) {
|
||||||
@ -47,6 +72,12 @@ public class GrantDto {
|
|||||||
this.comment = grant.getComment();
|
this.comment = grant.getComment();
|
||||||
this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject());
|
this.project = grant.getProject() == null ? null : new ProjectDto(grant.getProject());
|
||||||
this.applicationFileName = grant.getApplication() == null ? null : grant.getApplication().getName();
|
this.applicationFileName = grant.getApplication() == null ? null : grant.getApplication().getName();
|
||||||
|
this.authorIds = convert(grant.getAuthors(), user -> user.getId());
|
||||||
|
this.authors = convert(grant.getAuthors(), UserDto::new);
|
||||||
|
this.leaderId = grant.getLeader().getId();
|
||||||
|
this.wasLeader = false;
|
||||||
|
this.hasAge = false;
|
||||||
|
this.hasDegree = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
@ -104,4 +135,59 @@ public class GrantDto {
|
|||||||
public void setApplicationFileName(String applicationFileName) {
|
public void setApplicationFileName(String applicationFileName) {
|
||||||
this.applicationFileName = applicationFileName;
|
this.applicationFileName = applicationFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getAuthorIds() {
|
||||||
|
return authorIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorIds(Set<Integer> authorIds) {
|
||||||
|
this.authorIds = authorIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserDto> getAuthors() {
|
||||||
|
return authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthors(Set<UserDto> authors) {
|
||||||
|
this.authors = authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorsString() {
|
||||||
|
return StringUtils.abbreviate(authors
|
||||||
|
.stream()
|
||||||
|
.map(author -> author.getLastName())
|
||||||
|
.collect(Collectors.joining(", ")), MAX_AUTHORS_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLeaderId() {
|
||||||
|
return leaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeaderId(Integer leaderId) {
|
||||||
|
this.leaderId = leaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWasLeader() {
|
||||||
|
return wasLeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWasLeader(boolean wasLeader) {
|
||||||
|
this.wasLeader = wasLeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasAge() {
|
||||||
|
return hasAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasAge(boolean hasAge) {
|
||||||
|
this.hasAge = hasAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasDegree() {
|
||||||
|
return hasDegree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasDegree(boolean hasDegree) {
|
||||||
|
this.hasDegree = hasDegree;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package ru.ulstu.grant.model;
|
|
||||||
|
|
||||||
public class GrantStatusDto {
|
|
||||||
private final String id;
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public GrantStatusDto(Grant.GrantStatus status) {
|
|
||||||
this.id = status.name();
|
|
||||||
this.name = status.getStatusName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,9 @@ package ru.ulstu.grant.repository;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import ru.ulstu.grant.model.Grant;
|
import ru.ulstu.grant.model.Grant;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface GrantRepository extends JpaRepository<Grant, Integer> {
|
public interface GrantRepository extends JpaRepository<Grant, Integer> {
|
||||||
|
|
||||||
|
List<Grant> findByStatus(Grant.GrantStatus status);
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,14 @@ import ru.ulstu.grant.repository.GrantRepository;
|
|||||||
import ru.ulstu.project.model.Project;
|
import ru.ulstu.project.model.Project;
|
||||||
import ru.ulstu.project.model.ProjectDto;
|
import ru.ulstu.project.model.ProjectDto;
|
||||||
import ru.ulstu.project.service.ProjectService;
|
import ru.ulstu.project.service.ProjectService;
|
||||||
|
import ru.ulstu.user.model.User;
|
||||||
|
import ru.ulstu.user.service.UserService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||||
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
||||||
@ -30,15 +33,18 @@ public class GrantService {
|
|||||||
private final ProjectService projectService;
|
private final ProjectService projectService;
|
||||||
private final DeadlineService deadlineService;
|
private final DeadlineService deadlineService;
|
||||||
private final FileService fileService;
|
private final FileService fileService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
public GrantService(GrantRepository grantRepository,
|
public GrantService(GrantRepository grantRepository,
|
||||||
FileService fileService,
|
FileService fileService,
|
||||||
DeadlineService deadlineService,
|
DeadlineService deadlineService,
|
||||||
ProjectService projectService) {
|
ProjectService projectService,
|
||||||
|
UserService userService) {
|
||||||
this.grantRepository = grantRepository;
|
this.grantRepository = grantRepository;
|
||||||
this.projectService = projectService;
|
|
||||||
this.fileService = fileService;
|
this.fileService = fileService;
|
||||||
this.deadlineService = deadlineService;
|
this.deadlineService = deadlineService;
|
||||||
|
this.projectService = projectService;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Grant> findAll() {
|
public List<Grant> findAll() {
|
||||||
@ -73,6 +79,13 @@ public class GrantService {
|
|||||||
if (grantDto.getApplicationFileName() != null) {
|
if (grantDto.getApplicationFileName() != null) {
|
||||||
grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName()));
|
grant.setApplication(fileService.createFileFromTmp(grantDto.getApplicationFileName()));
|
||||||
}
|
}
|
||||||
|
grant.getAuthors().clear();
|
||||||
|
if (grantDto.getAuthorIds() != null && !grantDto.getAuthorIds().isEmpty()) {
|
||||||
|
grantDto.getAuthorIds().forEach(authorIds -> grant.getAuthors().add(userService.findById(authorIds)));
|
||||||
|
}
|
||||||
|
if (grantDto.getLeaderId() != null) {
|
||||||
|
grant.setLeader(userService.findById(grantDto.getLeaderId()));
|
||||||
|
}
|
||||||
return grant;
|
return grant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +97,6 @@ public class GrantService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public Integer update(GrantDto grantDto) throws IOException {
|
public Integer update(GrantDto grantDto) throws IOException {
|
||||||
Grant grant = grantRepository.findOne(grantDto.getId());
|
Grant grant = grantRepository.findOne(grantDto.getId());
|
||||||
Grant.GrantStatus oldStatus = grant.getStatus();
|
|
||||||
if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) {
|
if (grantDto.getApplicationFileName() != null && grant.getApplication() != null) {
|
||||||
fileService.deleteFile(grant.getApplication());
|
fileService.deleteFile(grant.getApplication());
|
||||||
}
|
}
|
||||||
@ -98,7 +110,6 @@ public class GrantService {
|
|||||||
if (grant.getApplication() != null) {
|
if (grant.getApplication() != null) {
|
||||||
fileService.deleteFile(grant.getApplication());
|
fileService.deleteFile(grant.getApplication());
|
||||||
}
|
}
|
||||||
//возможно при удалении гранта будет удаляться и проект, к нему привязанный
|
|
||||||
grantRepository.delete(grant);
|
grantRepository.delete(grant);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,13 +118,15 @@ public class GrantService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Grant create(String title, Project projectId, Date deadlineDate) {
|
public Grant create(String title, Project projectId, Date deadlineDate, User user) {
|
||||||
Grant grant = new Grant();
|
Grant grant = new Grant();
|
||||||
grant.setTitle(title);
|
grant.setTitle(title);
|
||||||
grant.setComment("Комментарий к гранту 1");
|
grant.setComment("Комментарий к гранту 1");
|
||||||
grant.setProject(projectId);
|
grant.setProject(projectId);
|
||||||
grant.setStatus(APPLICATION);
|
grant.setStatus(APPLICATION);
|
||||||
grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн"));
|
grant.getDeadlines().add(new Deadline(deadlineDate, "первый дедлайн"));
|
||||||
|
grant.getAuthors().add(user);
|
||||||
|
grant.setLeader(user);
|
||||||
grant = grantRepository.save(grant);
|
grant = grantRepository.save(grant);
|
||||||
return grant;
|
return grant;
|
||||||
}
|
}
|
||||||
@ -125,4 +138,22 @@ public class GrantService {
|
|||||||
update(grantDto);
|
update(grantDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<User> getGrantAuthors(GrantDto grantDto) {
|
||||||
|
List<User> filteredUsers = userService.filterByAgeAndDegree(grantDto.isHasAge(), grantDto.isHasDegree());
|
||||||
|
if (grantDto.isWasLeader()) {
|
||||||
|
filteredUsers = filteredUsers
|
||||||
|
.stream()
|
||||||
|
.filter(getCompletedGrantLeaders()::contains)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return filteredUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<User> getCompletedGrantLeaders() {
|
||||||
|
return grantRepository.findByStatus(Grant.GrantStatus.COMPLETED)
|
||||||
|
.stream()
|
||||||
|
.map(Grant::getLeader)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package ru.ulstu.paper.controller;
|
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.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
import org.springframework.validation.Errors;
|
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.Paper;
|
||||||
import ru.ulstu.paper.model.PaperDto;
|
import ru.ulstu.paper.model.PaperDto;
|
||||||
import ru.ulstu.paper.model.PaperFilterDto;
|
import ru.ulstu.paper.model.PaperFilterDto;
|
||||||
|
import ru.ulstu.paper.service.LatexService;
|
||||||
import ru.ulstu.paper.service.PaperService;
|
import ru.ulstu.paper.service.PaperService;
|
||||||
import ru.ulstu.user.model.User;
|
import ru.ulstu.user.model.User;
|
||||||
import springfox.documentation.annotations.ApiIgnore;
|
import springfox.documentation.annotations.ApiIgnore;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.springframework.util.StringUtils.isEmpty;
|
import static org.springframework.util.StringUtils.isEmpty;
|
||||||
|
|
||||||
|
|
||||||
@ -32,9 +38,11 @@ import static org.springframework.util.StringUtils.isEmpty;
|
|||||||
@ApiIgnore
|
@ApiIgnore
|
||||||
public class PaperController {
|
public class PaperController {
|
||||||
private final PaperService paperService;
|
private final PaperService paperService;
|
||||||
|
private final LatexService latexService;
|
||||||
|
|
||||||
public PaperController(PaperService paperService) {
|
public PaperController(PaperService paperService, LatexService latexService) {
|
||||||
this.paperService = paperService;
|
this.paperService = paperService;
|
||||||
|
this.latexService = latexService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/papers")
|
@GetMapping("/papers")
|
||||||
@ -111,6 +119,14 @@ public class PaperController {
|
|||||||
return years;
|
return years;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/generatePdf")
|
||||||
|
public ResponseEntity<byte[]> 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) {
|
private void filterEmptyDeadlines(PaperDto paperDto) {
|
||||||
paperDto.setDeadlines(paperDto.getDeadlines().stream()
|
paperDto.setDeadlines(paperDto.getDeadlines().stream()
|
||||||
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
|
.filter(dto -> dto.getDate() != null || !isEmpty(dto.getDescription()))
|
||||||
|
@ -91,6 +91,9 @@ public class Paper extends BaseEntity implements UserContainer {
|
|||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
private Set<User> authors = new HashSet<>();
|
private Set<User> authors = new HashSet<>();
|
||||||
|
|
||||||
|
@Column(name = "latex_text")
|
||||||
|
private String latexText;
|
||||||
|
|
||||||
public PaperStatus getStatus() {
|
public PaperStatus getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -179,6 +182,14 @@ public class Paper extends BaseEntity implements UserContainer {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLatexText() {
|
||||||
|
return latexText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLatexText(String latexText) {
|
||||||
|
this.latexText = latexText;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<User> getUsers() {
|
public Set<User> getUsers() {
|
||||||
return getAuthors();
|
return getAuthors();
|
||||||
|
@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
import ru.ulstu.file.model.FileDataDto;
|
|
||||||
import ru.ulstu.deadline.model.Deadline;
|
import ru.ulstu.deadline.model.Deadline;
|
||||||
|
import ru.ulstu.file.model.FileDataDto;
|
||||||
import ru.ulstu.user.model.UserDto;
|
import ru.ulstu.user.model.UserDto;
|
||||||
|
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
@ -36,6 +36,7 @@ public class PaperDto {
|
|||||||
private Set<Integer> authorIds;
|
private Set<Integer> authorIds;
|
||||||
private Set<UserDto> authors;
|
private Set<UserDto> authors;
|
||||||
private Integer filterAuthorId;
|
private Integer filterAuthorId;
|
||||||
|
private String latexText;
|
||||||
|
|
||||||
public PaperDto() {
|
public PaperDto() {
|
||||||
deadlines.add(new Deadline());
|
deadlines.add(new Deadline());
|
||||||
@ -49,6 +50,7 @@ public class PaperDto {
|
|||||||
@JsonProperty("updateDate") Date updateDate,
|
@JsonProperty("updateDate") Date updateDate,
|
||||||
@JsonProperty("deadlines") List<Deadline> deadlines,
|
@JsonProperty("deadlines") List<Deadline> deadlines,
|
||||||
@JsonProperty("comment") String comment,
|
@JsonProperty("comment") String comment,
|
||||||
|
@JsonProperty("latex_text") String latexText,
|
||||||
@JsonProperty("url") String url,
|
@JsonProperty("url") String url,
|
||||||
@JsonProperty("locked") Boolean locked,
|
@JsonProperty("locked") Boolean locked,
|
||||||
@JsonProperty("files") List<FileDataDto> files,
|
@JsonProperty("files") List<FileDataDto> files,
|
||||||
@ -62,6 +64,7 @@ public class PaperDto {
|
|||||||
this.deadlines = deadlines;
|
this.deadlines = deadlines;
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
this.latexText = latexText;
|
||||||
this.locked = locked;
|
this.locked = locked;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
this.authors = authors;
|
this.authors = authors;
|
||||||
@ -76,6 +79,7 @@ public class PaperDto {
|
|||||||
this.deadlines = paper.getDeadlines();
|
this.deadlines = paper.getDeadlines();
|
||||||
this.comment = paper.getComment();
|
this.comment = paper.getComment();
|
||||||
this.url = paper.getUrl();
|
this.url = paper.getUrl();
|
||||||
|
this.latexText = paper.getLatexText();
|
||||||
this.locked = paper.getLocked();
|
this.locked = paper.getLocked();
|
||||||
this.files = convert(paper.getFiles(), FileDataDto::new);
|
this.files = convert(paper.getFiles(), FileDataDto::new);
|
||||||
this.authorIds = convert(paper.getAuthors(), user -> user.getId());
|
this.authorIds = convert(paper.getAuthors(), user -> user.getId());
|
||||||
@ -178,6 +182,14 @@ public class PaperDto {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLatexText() {
|
||||||
|
return latexText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLatexText(String latexText) {
|
||||||
|
this.latexText = latexText;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAuthorsString() {
|
public String getAuthorsString() {
|
||||||
return StringUtils.abbreviate(authors
|
return StringUtils.abbreviate(authors
|
||||||
.stream()
|
.stream()
|
||||||
|
72
src/main/java/ru/ulstu/paper/service/LatexService.java
Normal file
72
src/main/java/ru/ulstu/paper/service/LatexService.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
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 String errorMessage;
|
||||||
|
private File pdfFile;
|
||||||
|
private FileService fileService;
|
||||||
|
private final String pdfLatexError = "Errors occurred while executing pdfLaTeX.";
|
||||||
|
private final String bibtexError = "Errors occurred while executing bibtex.";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,7 @@ public class PaperService {
|
|||||||
private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException {
|
private Paper copyFromDto(Paper paper, PaperDto paperDto) throws IOException {
|
||||||
paper.setComment(paperDto.getComment());
|
paper.setComment(paperDto.getComment());
|
||||||
paper.setUrl(paperDto.getUrl());
|
paper.setUrl(paperDto.getUrl());
|
||||||
|
paper.setLatexText(paperDto.getLatexText());
|
||||||
paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate());
|
paper.setCreateDate(paper.getCreateDate() == null ? new Date() : paper.getCreateDate());
|
||||||
paper.setLocked(paperDto.getLocked());
|
paper.setLocked(paperDto.getLocked());
|
||||||
paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus());
|
paper.setStatus(paperDto.getStatus() == null ? DRAFT : paperDto.getStatus());
|
||||||
|
@ -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<Project.ProjectStatus> getProjectStatuses() {
|
||||||
|
return projectService.getProjectStatuses();
|
||||||
|
}
|
||||||
|
}
|
@ -3,24 +3,60 @@ package ru.ulstu.project.model;
|
|||||||
import org.hibernate.validator.constraints.NotBlank;
|
import org.hibernate.validator.constraints.NotBlank;
|
||||||
import ru.ulstu.core.model.BaseEntity;
|
import ru.ulstu.core.model.BaseEntity;
|
||||||
import ru.ulstu.deadline.model.Deadline;
|
import ru.ulstu.deadline.model.Deadline;
|
||||||
|
import ru.ulstu.grant.model.Grant;
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class Project extends BaseEntity {
|
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
|
@NotBlank
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private ProjectStatus status = ProjectStatus.APPLICATION;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String description;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL)
|
@OneToMany(cascade = CascadeType.ALL)
|
||||||
@JoinColumn(name = "project_id")
|
@JoinColumn(name = "project_id")
|
||||||
private List<Deadline> deadlines = new ArrayList<>();
|
private List<Deadline> deadlines = new ArrayList<>();
|
||||||
|
|
||||||
|
@ManyToOne(cascade = CascadeType.ALL)
|
||||||
|
@JoinColumn(name = "grant_id")
|
||||||
|
private Grant grant;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String repository;
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
@ -29,6 +65,38 @@ public class Project extends BaseEntity {
|
|||||||
this.title = title;
|
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<Deadline> getDeadlines() {
|
public List<Deadline> getDeadlines() {
|
||||||
return deadlines;
|
return deadlines;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
import ru.ulstu.deadline.model.Deadline;
|
import ru.ulstu.deadline.model.Deadline;
|
||||||
|
import ru.ulstu.grant.model.GrantDto;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -13,8 +14,11 @@ public class ProjectDto {
|
|||||||
|
|
||||||
@NotEmpty
|
@NotEmpty
|
||||||
private String title;
|
private String title;
|
||||||
|
private Project.ProjectStatus status;
|
||||||
|
private String description;
|
||||||
private List<Deadline> deadlines = new ArrayList<>();
|
private List<Deadline> deadlines = new ArrayList<>();
|
||||||
|
private GrantDto grant;
|
||||||
|
private String repository;
|
||||||
|
|
||||||
public ProjectDto() {
|
public ProjectDto() {
|
||||||
}
|
}
|
||||||
@ -26,9 +30,17 @@ public class ProjectDto {
|
|||||||
@JsonCreator
|
@JsonCreator
|
||||||
public ProjectDto(@JsonProperty("id") Integer id,
|
public ProjectDto(@JsonProperty("id") Integer id,
|
||||||
@JsonProperty("title") String title,
|
@JsonProperty("title") String title,
|
||||||
|
@JsonProperty("status") Project.ProjectStatus status,
|
||||||
|
@JsonProperty("description") String description,
|
||||||
|
@JsonProperty("grant") GrantDto grant,
|
||||||
|
@JsonProperty("repository") String repository,
|
||||||
@JsonProperty("deadlines") List<Deadline> deadlines) {
|
@JsonProperty("deadlines") List<Deadline> deadlines) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.status = status;
|
||||||
|
this.description = description;
|
||||||
|
this.grant = grant;
|
||||||
|
this.repository = repository;
|
||||||
this.deadlines = deadlines;
|
this.deadlines = deadlines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +48,10 @@ public class ProjectDto {
|
|||||||
public ProjectDto(Project project) {
|
public ProjectDto(Project project) {
|
||||||
this.id = project.getId();
|
this.id = project.getId();
|
||||||
this.title = project.getTitle();
|
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();
|
this.deadlines = project.getDeadlines();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +71,38 @@ public class ProjectDto {
|
|||||||
this.title = title;
|
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<Deadline> getDeadlines() {
|
public List<Deadline> getDeadlines() {
|
||||||
return deadlines;
|
return deadlines;
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,21 @@ package ru.ulstu.project.service;
|
|||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.thymeleaf.util.StringUtils;
|
||||||
import ru.ulstu.deadline.service.DeadlineService;
|
import ru.ulstu.deadline.service.DeadlineService;
|
||||||
import ru.ulstu.project.model.Project;
|
import ru.ulstu.project.model.Project;
|
||||||
import ru.ulstu.project.model.ProjectDto;
|
import ru.ulstu.project.model.ProjectDto;
|
||||||
import ru.ulstu.project.repository.ProjectRepository;
|
import ru.ulstu.project.repository.ProjectRepository;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||||
|
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ProjectService {
|
public class ProjectService {
|
||||||
|
private final static int MAX_DISPLAY_SIZE = 40;
|
||||||
|
|
||||||
private final ProjectRepository projectRepository;
|
private final ProjectRepository projectRepository;
|
||||||
private final DeadlineService deadlineService;
|
private final DeadlineService deadlineService;
|
||||||
@ -27,6 +31,20 @@ public class ProjectService {
|
|||||||
return projectRepository.findAll();
|
return projectRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ProjectDto> findAllDto() {
|
||||||
|
List<ProjectDto> 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.findOne(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Project.ProjectStatus> getProjectStatuses() {
|
||||||
|
return Arrays.asList(Project.ProjectStatus.values());
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Project create(ProjectDto projectDto) {
|
public Project create(ProjectDto projectDto) {
|
||||||
Project newProject = copyFromDto(new Project(), projectDto);
|
Project newProject = copyFromDto(new Project(), projectDto);
|
||||||
|
@ -7,6 +7,8 @@ import ru.ulstu.core.model.BaseEntity;
|
|||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.JoinTable;
|
import javax.persistence.JoinTable;
|
||||||
import javax.persistence.ManyToMany;
|
import javax.persistence.ManyToMany;
|
||||||
@ -85,6 +87,28 @@ public class User extends BaseEntity {
|
|||||||
@BatchSize(size = 20)
|
@BatchSize(size = 20)
|
||||||
private Set<UserRole> roles;
|
private Set<UserRole> roles;
|
||||||
|
|
||||||
|
@Column(name = "birth_date")
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date birthDate;
|
||||||
|
|
||||||
|
public enum UserDegree {
|
||||||
|
CANDIDATE("Кандидат технических наук"),
|
||||||
|
DOCTOR("Доктор технических наук");
|
||||||
|
|
||||||
|
private String degreeName;
|
||||||
|
|
||||||
|
UserDegree(String degreeName) {
|
||||||
|
this.degreeName = degreeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDegreeName() {
|
||||||
|
return degreeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private UserDegree degree;
|
||||||
|
|
||||||
public User() {
|
public User() {
|
||||||
roles = new HashSet<>();
|
roles = new HashSet<>();
|
||||||
activated = false;
|
activated = false;
|
||||||
@ -189,6 +213,22 @@ public class User extends BaseEntity {
|
|||||||
this.patronymic = patronymic;
|
this.patronymic = patronymic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Date getBirthDate() {
|
||||||
|
return birthDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBirthDate(Date birthDate) {
|
||||||
|
this.birthDate = birthDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDegree getDegree() {
|
||||||
|
return degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDegree(UserDegree degree) {
|
||||||
|
this.degree = degree;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUserAbbreviate() {
|
public String getUserAbbreviate() {
|
||||||
return String.format(USER_ABBREVIATE_TEMPLATE,
|
return String.format(USER_ABBREVIATE_TEMPLATE,
|
||||||
lastName == null ? "" : lastName,
|
lastName == null ? "" : lastName,
|
||||||
|
@ -15,6 +15,7 @@ import ru.ulstu.user.controller.UserController;
|
|||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -70,6 +71,10 @@ public class UserDto implements OdinDto {
|
|||||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||||
private String passwordConfirm;
|
private String passwordConfirm;
|
||||||
|
|
||||||
|
private Date birthDate;
|
||||||
|
|
||||||
|
private User.UserDegree degree;
|
||||||
|
|
||||||
public UserDto() {
|
public UserDto() {
|
||||||
activated = false;
|
activated = false;
|
||||||
roles = new LinkedHashSet<>();
|
roles = new LinkedHashSet<>();
|
||||||
@ -86,6 +91,8 @@ public class UserDto implements OdinDto {
|
|||||||
this.roles.addAll(user.getRoles().stream()
|
this.roles.addAll(user.getRoles().stream()
|
||||||
.map(UserRoleDto::new)
|
.map(UserRoleDto::new)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
this.birthDate = user.getBirthDate();
|
||||||
|
this.degree = user.getDegree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getId() {
|
public Integer getId() {
|
||||||
@ -163,6 +170,22 @@ public class UserDto implements OdinDto {
|
|||||||
return passwordConfirm;
|
return passwordConfirm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Date getBirthDate() {
|
||||||
|
return birthDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBirthDate(Date birthDate) {
|
||||||
|
this.birthDate = birthDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User.UserDegree getDegree() {
|
||||||
|
return degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDegree(User.UserDegree degree) {
|
||||||
|
this.degree = degree;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isPasswordsValid() {
|
public boolean isPasswordsValid() {
|
||||||
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
|
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
|
||||||
@ -188,6 +211,8 @@ public class UserDto implements OdinDto {
|
|||||||
", roles=" + roles +
|
", roles=" + roles +
|
||||||
", password='" + password + '\'' +
|
", password='" + password + '\'' +
|
||||||
", passwordConfirm='" + passwordConfirm + '\'' +
|
", passwordConfirm='" + passwordConfirm + '\'' +
|
||||||
|
", birthDate='" + birthDate + '\'' +
|
||||||
|
", degree='" + degree + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ package ru.ulstu.user.repository;
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.EntityGraph;
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import ru.ulstu.user.model.User;
|
import ru.ulstu.user.model.User;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -25,4 +27,11 @@ public interface UserRepository extends JpaRepository<User, Integer> {
|
|||||||
|
|
||||||
@EntityGraph(attributePaths = "roles")
|
@EntityGraph(attributePaths = "roles")
|
||||||
User findOneWithRolesByLogin(String login);
|
User findOneWithRolesByLogin(String login);
|
||||||
|
|
||||||
|
@Query("SELECT u FROM User u " +
|
||||||
|
"WHERE (YEAR(CURRENT_DATE) - YEAR(u.birthDate) < 35 OR :hasAge = FALSE) " +
|
||||||
|
"AND (u.degree = 'CANDIDATE' OR :hasDegree = FALSE)" +
|
||||||
|
"ORDER BY u.lastName")
|
||||||
|
List<User> filterByAgeAndDegree(@Param("hasAge") boolean hasAge,
|
||||||
|
@Param("hasDegree") boolean hasDegree);
|
||||||
}
|
}
|
||||||
|
@ -324,4 +324,8 @@ public class UserService implements UserDetailsService {
|
|||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<User> filterByAgeAndDegree(boolean hasDegree, boolean hasAge) {
|
||||||
|
return userRepository.filterByAgeAndDegree(hasDegree, hasAge);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
14
src/main/resources/db/changelog-20190323_000001-schema.xml
Normal file
14
src/main/resources/db/changelog-20190323_000001-schema.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="masha" id="20190323_000000-1">
|
||||||
|
<addColumn tableName="paper">
|
||||||
|
<column name="latex_text" type="varchar"/>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="file">
|
||||||
|
<column name="is_latex_attach" type="boolean"/>
|
||||||
|
</addColumn>
|
||||||
|
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
26
src/main/resources/db/changelog-20190402_000000-schema.xml
Normal file
26
src/main/resources/db/changelog-20190402_000000-schema.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="tanya" id="20190402_000000-1">
|
||||||
|
<addColumn tableName="grants">
|
||||||
|
<column name="leader_id" type="integer"></column>
|
||||||
|
</addColumn>
|
||||||
|
<addForeignKeyConstraint baseTableName="grants" baseColumnNames="leader_id"
|
||||||
|
constraintName="fk_grants_leader_id" referencedTableName="users"
|
||||||
|
referencedColumnNames="id"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="tanya" id="20190402_000000-2">
|
||||||
|
<createTable tableName="grants_authors">
|
||||||
|
<column name="grant_id" type="integer"/>
|
||||||
|
<column name="authors_id" type="integer"/>
|
||||||
|
</createTable>
|
||||||
|
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="grant_id"
|
||||||
|
constraintName="fk_grants_grants_authors" referencedTableName="grants"
|
||||||
|
referencedColumnNames="id"/>
|
||||||
|
<addForeignKeyConstraint baseTableName="grants_authors" baseColumnNames="authors_id"
|
||||||
|
constraintName="fk_user_grants_authors" referencedTableName="users"
|
||||||
|
referencedColumnNames="id"/>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
13
src/main/resources/db/changelog-20190404_000000-schema.xml
Normal file
13
src/main/resources/db/changelog-20190404_000000-schema.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="tanya" id="20190404_000000-1">
|
||||||
|
<addColumn tableName="users">
|
||||||
|
<column name="birth_date" type="timestamp"></column>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="users">
|
||||||
|
<column name="degree" type="varchar(255)"></column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
22
src/main/resources/db/changelog-20190418_000000-schema.xml
Normal file
22
src/main/resources/db/changelog-20190418_000000-schema.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||||
|
<changeSet author="anton" id="20190418_000000-1">
|
||||||
|
<addColumn tableName="project">
|
||||||
|
<column name="status" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="project">
|
||||||
|
<column name="description" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
<addColumn tableName="project">
|
||||||
|
<column name="grant_id" type="integer"/>
|
||||||
|
</addColumn>
|
||||||
|
<addForeignKeyConstraint baseTableName="project" baseColumnNames="grant_id"
|
||||||
|
constraintName="fk_project_grant_id" referencedTableName="grants"
|
||||||
|
referencedColumnNames="id"/>
|
||||||
|
<addColumn tableName="project">
|
||||||
|
<column name="repository" type="varchar(255)"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
@ -25,5 +25,9 @@
|
|||||||
<include file="db/changelog-20190331_000010-schema.xml"/>
|
<include file="db/changelog-20190331_000010-schema.xml"/>
|
||||||
<include file="db/changelog-20190410_000000-schema.xml"/>
|
<include file="db/changelog-20190410_000000-schema.xml"/>
|
||||||
<include file="db/changelog-20190417_000000-schema.xml"/>
|
<include file="db/changelog-20190417_000000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20190418_000000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20190323_000001-schema.xml"/>
|
||||||
<include file="db/common/changelog-20190312_130000-schema.xml"/>
|
<include file="db/common/changelog-20190312_130000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20190402_000000-schema.xml"/>
|
||||||
|
<include file="db/changelog-20190404_000000-schema.xml"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
BIN
src/main/resources/drivers/chromedriver
Normal file
BIN
src/main/resources/drivers/chromedriver
Normal file
Binary file not shown.
BIN
src/main/resources/drivers/chromedriver.exe
Normal file
BIN
src/main/resources/drivers/chromedriver.exe
Normal file
Binary file not shown.
BIN
src/main/resources/drivers/geckodriver
Normal file
BIN
src/main/resources/drivers/geckodriver
Normal file
Binary file not shown.
BIN
src/main/resources/drivers/geckodriver.exe
Normal file
BIN
src/main/resources/drivers/geckodriver.exe
Normal file
Binary file not shown.
12
src/main/resources/public/css/grant.css
Normal file
12
src/main/resources/public/css/grant.css
Normal file
@ -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;
|
||||||
|
}
|
@ -1,5 +1,9 @@
|
|||||||
#files-list .row > div:nth-child(6) {
|
#files-list .row > div:nth-child(7) {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
var urlFileUpload = "/api/1.0/files/uploadTmpFile";
|
var urlFileUpload = "/api/1.0/files/uploadTmpFile";
|
||||||
var urlFileDownload = "/api/1.0/files/download/";
|
var urlFileDownload = "/api/1.0/files/download/";
|
||||||
|
var urlPdfGenerating = "/papers/generatePdf";
|
||||||
var urlFileDownloadTmp = "/api/1.0/files/download-tmp/";
|
var urlFileDownloadTmp = "/api/1.0/files/download-tmp/";
|
||||||
|
|
||||||
/* exported MessageTypesEnum */
|
/* exported MessageTypesEnum */
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/>
|
<span th:replace="grants/fragments/grantStatusFragment :: grantStatus(grantStatus=${grant.status})"/>
|
||||||
<a th:href="@{'grant?id='+${grant.id}}">
|
<a th:href="@{'grant?id='+${grant.id}}">
|
||||||
<span class="h6" th:text="${grant.title}"/>
|
<span class="h6" th:text="${grant.title}"/>
|
||||||
<span class="text-muted" th:text="${grant.comment}"/>
|
<span class="text-muted" th:text="${grant.authorsString}"/>
|
||||||
</a>
|
</a>
|
||||||
<input class="id-class" type="hidden" th:value="${grant.id}"/>
|
<input class="id-class" type="hidden" th:value="${grant.id}"/>
|
||||||
<a class="remove-paper pull-right d-none" th:href="@{'/grants/delete/'+${grant.id}}"
|
<a class="remove-paper pull-right d-none" th:href="@{'/grants/delete/'+${grant.id}}"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x">
|
<span th:fragment="grantStatus (grantStatus)" class="fa-stack fa-1x">
|
||||||
<th:block th:switch="${grantStatus.name()}">
|
<th:block th:switch="${grantStatus.name()}">
|
||||||
<div th:case="'APPLICATION'">
|
<div th:case="'APPLICATION'">
|
||||||
<i class="fa fa-circle fa-stack-2x text-draft"></i>
|
<i class="fa fa-circle fa-stack-2x text-draft"></i>
|
||||||
|
@ -3,13 +3,12 @@
|
|||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
|
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
|
||||||
<head>
|
<head>
|
||||||
|
<link rel="stylesheet" href="../css/grant.css"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
|
<section id="grant">
|
||||||
<section id="paper">
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 text-center">
|
<div class="col-lg-12 text-center">
|
||||||
@ -17,6 +16,7 @@
|
|||||||
<div th:replace="grants/fragments/grantNavigationFragment"/>
|
<div th:replace="grants/fragments/grantNavigationFragment"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<hr/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<form id="grant-form" method="post" th:action="@{'/grants/grant?id='+ *{id == null ? '' : id} + ''}"
|
<form id="grant-form" method="post" th:action="@{'/grants/grant?id='+ *{id == null ? '' : id} + ''}"
|
||||||
@ -33,7 +33,6 @@
|
|||||||
class="alert alert-danger">Incorrect title</p>
|
class="alert alert-danger">Incorrect title</p>
|
||||||
<p class="help-block text-danger"></p>
|
<p class="help-block text-danger"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="status">Статус:</label>
|
<label for="status">Статус:</label>
|
||||||
<select class="form-control" th:field="*{status}" id="status">
|
<select class="form-control" th:field="*{status}" id="status">
|
||||||
@ -51,11 +50,11 @@
|
|||||||
<label>Дедлайны показателей:</label>
|
<label>Дедлайны показателей:</label>
|
||||||
<div class="row" th:each="deadline, rowStat : *{deadlines}">
|
<div class="row" th:each="deadline, rowStat : *{deadlines}">
|
||||||
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
|
<input type="hidden" th:field="*{deadlines[__${rowStat.index}__].id}"/>
|
||||||
<div class="col-6">
|
<div class="col-6 div-deadline-date">
|
||||||
<input type="date" class="form-control" name="deadline"
|
<input type="date" class="form-control form-deadline-date" name="deadline"
|
||||||
th:field="*{deadlines[__${rowStat.index}__].date}"/>
|
th:field="*{deadlines[__${rowStat.index}__].date}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4 div-deadline-description">
|
||||||
<input class="form-control" type="text" placeholder="Описание"
|
<input class="form-control" type="text" placeholder="Описание"
|
||||||
th:field="*{deadlines[__${rowStat.index}__].description}"/>
|
th:field="*{deadlines[__${rowStat.index}__].description}"/>
|
||||||
</div>
|
</div>
|
||||||
@ -72,31 +71,101 @@
|
|||||||
class="alert alert-danger">Incorrect title</p>
|
class="alert alert-danger">Incorrect title</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary" value="Добавить
|
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
|
||||||
дедлайн"/>
|
value="Добавить дедлайн"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="loader">Загрузить заявку:</label>
|
<label for="loader">Загрузить заявку:</label>
|
||||||
<div id="loader">
|
<div id="loader">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<label data-toggle="collapse"
|
||||||
|
href="#collapse-filter"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="collapse-filter">Фильтр рабочей группы
|
||||||
|
</label>
|
||||||
|
<div th:class="${grantDto.wasLeader || grantDto.hasAge || grantDto.hasDegree} ?
|
||||||
|
'form-check' : 'form-check collapse'" id="collapse-filter">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f1"
|
||||||
|
th:field="*{wasLeader}" th:onclick="|$('#filter').click();|"/>
|
||||||
|
<label class="form-check-label" for="f1">Был руководителем проекта</label>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f2"
|
||||||
|
th:field="*{hasAge}" th:onclick="|$('#filter').click();|"/>
|
||||||
|
<label class="form-check-label" for="f2">Младше 35 лет</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f3"
|
||||||
|
th:field="*{hasDegree}" th:onclick="|$('#filter').click();|"/>
|
||||||
|
<label class="form-check-label" for="f3">Cтепень к.т.н.</label>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f4"/>
|
||||||
|
<label class="form-check-label" for="f4">Более 3-х публикаций в
|
||||||
|
scopus</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f5"/>
|
||||||
|
<label class="form-check-label" for="f5">Наличие ВАК статей</label> <br/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input class="form-check-input" type="checkbox" id="f6"/>
|
||||||
|
<label class="form-check-label" for="f6">Наименьшая загруженность</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="submit" hidden="hidden" id="filter" name="filterUsers"
|
||||||
|
value="Применить фильтр"/>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Руководитель проекта:</label>
|
||||||
|
<select class="form-control" th:field="*{leaderId}" id="leaderId"
|
||||||
|
onchange="updateAuthors();">
|
||||||
|
<option selected="selected" hidden="hidden" th:value="-1">-- Выберите
|
||||||
|
руководителя --
|
||||||
|
</option>
|
||||||
|
<option th:each="leader : ${allAuthors}" th:value="${leader.id}"
|
||||||
|
th:text="${leader.lastName}"> Руководитель
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p th:if="${#fields.hasErrors('leaderId')}" th:errors="*{leaderId}"
|
||||||
|
class="alert alert-danger">Choose leader</p>
|
||||||
|
<p class="help-block text-danger"></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Участники гранта:</label>
|
||||||
|
<select class="selectpicker form-control" multiple="true"
|
||||||
|
title="-- Выберите участников --" id="authors"
|
||||||
|
th:field="*{authorIds}">
|
||||||
|
<option th:each="author : ${allAuthors}" th:value="${author.id}"
|
||||||
|
th:text="${author.lastName}"> Участник
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Список статей:</label>
|
<label>Список статей:</label>
|
||||||
<p><a href="./#" class="btn btn-primary" ><i class="fa fa-plus-circle" aria-hidden="true">
|
<p><a href="./#" class="btn btn-primary"><i class="fa fa-plus-circle"
|
||||||
|
aria-hidden="true">
|
||||||
</i> Добавить статью</a></p>
|
</i> Добавить статью</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div th:if="*{project} == null">
|
<div th:if="*{project} == null">
|
||||||
<input type="submit" name="createProject" class="btn btn-primary"
|
<input type="submit" name="createProject" class="btn btn-primary"
|
||||||
value="Добавить проект"/>
|
value="Добавить проект"/>
|
||||||
</div>
|
</div>
|
||||||
<input type = "hidden" th:field="*{project.id}"/>
|
<input type = "hidden" th:field="*{project.id}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -132,6 +201,17 @@
|
|||||||
$('.selectpicker').selectpicker();
|
$('.selectpicker').selectpicker();
|
||||||
});
|
});
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function updateAuthors() {
|
||||||
|
$("#authors").val('default');
|
||||||
|
$("#authors").selectpicker("refresh");
|
||||||
|
$("#authors").children('option:disabled').prop('disabled', false);
|
||||||
|
var lid = $("#leaderId option:selected").val();
|
||||||
|
$("#authors [value='" + lid + "']").attr("disabled", "disabled");
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 col-sm-6 portfolio-item">
|
<div class="col-md-4 col-sm-6 portfolio-item">
|
||||||
<a class="portfolio-link" data-toggle="modal" href="/projects">
|
<a class="portfolio-link" href="./projects/dashboard">
|
||||||
<div class="portfolio-hover">
|
<div class="portfolio-hover">
|
||||||
<div class="portfolio-hover-content">
|
<div class="portfolio-hover-content">
|
||||||
<i class="fa fa-arrow-right fa-3x"></i>
|
<i class="fa fa-arrow-right fa-3x"></i>
|
||||||
@ -82,7 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 col-sm-6 portfolio-item">
|
<div class="col-md-4 col-sm-6 portfolio-item">
|
||||||
<a class="portfolio-link" href="./students/tasks">
|
<a class="portfolio-link" href="./students/tasks">
|
||||||
<div class="portfolio-hover">
|
<div class="portfolio-hover">
|
||||||
<div class="portfolio-hover-content">
|
<div class="portfolio-hover-content">
|
||||||
<i class="fa fa-arrow-right fa-3x"></i>
|
<i class="fa fa-arrow-right fa-3x"></i>
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<body>
|
||||||
|
<div th:fragment="filesList (isLatexAttach)" th:remove="tag">
|
||||||
|
<th:block th:each="file, rowStat : *{files}">
|
||||||
|
|
||||||
|
<span th:if="${file.isLatexAttach == isLatexAttach}" th:remove="tag">
|
||||||
|
|
||||||
|
<div class="row" th:id="|files${rowStat.index}|"
|
||||||
|
th:style="${file.deleted} ? 'display: none;' :''">
|
||||||
|
<input type="hidden" th:field="*{files[__${rowStat.index}__].id}"/>
|
||||||
|
<input type="hidden"
|
||||||
|
th:field="*{files[__${rowStat.index}__].deleted}"/>
|
||||||
|
<input type="hidden"
|
||||||
|
th:field="*{files[__${rowStat.index}__].name}"/>
|
||||||
|
<input type="hidden"
|
||||||
|
th:field="*{files[__${rowStat.index}__].tmpFileName}"/>
|
||||||
|
<input type="hidden"
|
||||||
|
th:field="*{files[__${rowStat.index}__].isLatexAttach}"/>
|
||||||
|
<div class="col-2">
|
||||||
|
<a class="btn btn-danger float-right"
|
||||||
|
th:onclick="|$('#files${rowStat.index}\\.deleted').val('true'); $('#files${rowStat.index}').hide(); |">
|
||||||
|
<span aria-hidden="true"><i class="fa fa-times"/></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-10">
|
||||||
|
<a th:onclick="${file.id==null} ? 'downloadFile('+${file.tmpFileName}+',null,\''+${file.name}+'\')':
|
||||||
|
'downloadFile(null,'+${file.id}+',\''+${file.name}+'\')' "
|
||||||
|
href="javascript:void(0)"
|
||||||
|
th:text="*{files[__${rowStat.index}__].name}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -20,35 +20,47 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<form id="paper-form" method="post" th:action="@{'/papers/paper?id='+ *{id == null ? '' : id} + ''}"
|
<form name="paperform" id="paper-form" method="post"
|
||||||
|
th:action="@{'/papers/paper?id='+ *{id == null ? '' : id} + ''}"
|
||||||
th:object="${paperDto}">
|
th:object="${paperDto}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-7 col-sm-12">
|
<div class="col-md-7 col-sm-12">
|
||||||
<input type="hidden" name="id" th:field="*{id}"/>
|
<nav>
|
||||||
<div class="form-group">
|
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||||
<label for="title">Название:</label>
|
<a class="nav-item nav-link active" id="nav-main-tab" data-toggle="tab"
|
||||||
<input class="form-control" id="title" type="text"
|
href="#nav-main" role="tab" aria-controls="nav-main" aria-selected="true">Статья</a>
|
||||||
placeholder="Название статьи"
|
<a class="nav-item nav-link" id="nav-latex-tab" data-toggle="tab"
|
||||||
th:field="*{title}"/>
|
href="#nav-latex" role="tab" aria-controls="nav-latex" aria-selected="false">Latex</a>
|
||||||
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
|
</div>
|
||||||
class="alert alert-danger">Incorrect title</p>
|
</nav>
|
||||||
<p class="help-block text-danger"></p>
|
<div class="tab-content" id="nav-tabContent">
|
||||||
</div>
|
<div class="tab-pane fade show active" id="nav-main" role="tabpanel"
|
||||||
|
aria-labelledby="nav-main-tab">
|
||||||
|
<input type="hidden" name="id" th:field="*{id}"/>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Название:</label>
|
||||||
|
<input class="form-control" id="title" type="text"
|
||||||
|
placeholder="Название статьи"
|
||||||
|
th:field="*{title}"/>
|
||||||
|
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
|
||||||
|
class="alert alert-danger">Incorrect title</p>
|
||||||
|
<p class="help-block text-danger"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="status">Статус:</label>
|
<label for="status">Статус:</label>
|
||||||
<select class="form-control" th:field="*{status}" id="status">
|
<select class="form-control" th:field="*{status}" id="status">
|
||||||
<option th:each="status : ${allStatuses}" th:value="${status}"
|
<option th:each="status : ${allStatuses}" th:value="${status}"
|
||||||
th:text="${status.statusName}">Status
|
th:text="${status.statusName}">Status
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="comment">Комментарий:</label>
|
<label for="comment">Комментарий:</label>
|
||||||
<textarea class="form-control" rows="5" id="comment"
|
<textarea class="form-control" rows="5" id="comment"
|
||||||
th:field="*{comment}"></textarea>
|
th:field="*{comment}"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="title">Ссылка на сайт конференции:</label>
|
<label for="title">Ссылка на сайт конференции:</label>
|
||||||
@ -99,42 +111,52 @@
|
|||||||
class="alert alert-danger">Incorrect title</p>
|
class="alert alert-danger">Incorrect title</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" id="files-list">
|
<div class="form-group files-list" id="files-list">
|
||||||
<label>Файлы:</label>
|
<label>Файлы:</label>
|
||||||
<th:block th:each="file, rowStat : *{files}">
|
|
||||||
<div class="row" th:id="|files${rowStat.index}|"
|
<div th:replace="papers/fragments/paperFilesListFragment :: filesList(isLatexAttach = ${false})"/>
|
||||||
th:style="${file.deleted} ? 'display: none;' :''">
|
|
||||||
<input type="hidden" th:field="*{files[__${rowStat.index}__].id}"/>
|
</div>
|
||||||
<input type="hidden" th:field="*{files[__${rowStat.index}__].deleted}"/>
|
|
||||||
<input type="hidden" th:field="*{files[__${rowStat.index}__].name}"/>
|
<div class="form-check">
|
||||||
<input type="hidden" th:field="*{files[__${rowStat.index}__].tmpFileName}"/>
|
<input type="checkbox" class="form-check-input" id="locked"
|
||||||
<div class="col-2">
|
th:field="*{locked}"/>
|
||||||
<a class="btn btn-danger float-right"
|
<label class="form-check-label" for="locked">Заблокирована</label>
|
||||||
th:onclick="|$('#files${rowStat.index}\\.deleted').val('true'); $('#files${rowStat.index}').hide(); |">
|
</div>
|
||||||
<span aria-hidden="true"><i class="fa fa-times"/></span>
|
<div class="form-group">
|
||||||
</a>
|
<label for="loader">Загрузить статью:</label>
|
||||||
</div>
|
<div id="loader">
|
||||||
<div class="col-10">
|
|
||||||
<a th:onclick="${file.id==null} ? 'downloadFile('+${file.tmpFileName}+',null,\''+${file.name}+'\')':
|
|
||||||
'downloadFile(null,'+${file.id}+',\''+${file.name}+'\')' "
|
|
||||||
href="javascript:void(0)"
|
|
||||||
th:text="*{files[__${rowStat.index}__].name}">
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</th:block>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="nav-latex" role="tabpanel"
|
||||||
</div>
|
aria-labelledby="nav-profile-tab">
|
||||||
|
<div class="form-group">
|
||||||
<div class="form-check">
|
<label for="latex-text">Latex текст:</label>
|
||||||
<input type="checkbox" class="form-check-input" id="locked"
|
<textarea class="form-control" id="latex-text" type="text" rows="10"
|
||||||
th:field="*{locked}"/>
|
placeholder="Latex.."
|
||||||
<label class="form-check-label" for="locked">Заблокирована</label>
|
th:field="*{latexText}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group files-list" id="latex-files-list">
|
||||||
<label for="loader">Загрузить статью:</label>
|
<label>Файлы:</label>
|
||||||
<div id="loader">
|
<div th:replace="papers/fragments/paperFilesListFragment :: filesList(isLatexAttach = ${true})"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="latex-loader">Загрузить файлы:</label>
|
||||||
|
<div id="latex-loader">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button id="pdfBtn" class="btn btn-primary text-uppercase"
|
||||||
|
onclick="generatePDF()"
|
||||||
|
type="button">
|
||||||
|
<i id="pdfLoadingIcon" class='fa fa-circle-o-notch fa-spin'
|
||||||
|
style="display: none;"></i>
|
||||||
|
pdf
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -207,14 +229,26 @@
|
|||||||
showFeedbackMessage("Файл успешно загружен");
|
showFeedbackMessage("Файл успешно загружен");
|
||||||
console.debug(response);
|
console.debug(response);
|
||||||
|
|
||||||
addNewFile(response);
|
addNewFile(response, $("#files-list"), false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
new FileLoader({
|
||||||
|
div: "latex-loader",
|
||||||
|
url: urlFileUpload,
|
||||||
|
maxSize: -1,
|
||||||
|
extensions: [],
|
||||||
|
callback: function (response) {
|
||||||
|
showFeedbackMessage("Файл успешно загружен");
|
||||||
|
console.debug(response);
|
||||||
|
|
||||||
|
addNewFile(response, $("#latex-files-list"), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('.selectpicker').selectpicker();
|
$('.selectpicker').selectpicker();
|
||||||
});
|
});
|
||||||
/*]]>*/
|
/*]]>*/
|
||||||
function addNewFile(fileDto) {
|
function addNewFile(fileDto, listElement, isLatexAttach) {
|
||||||
var fileNumber = $("#files-list div.row").length;
|
var fileNumber = $('.files-list div.row').length;
|
||||||
|
|
||||||
var newFileRow = $("<div/>")
|
var newFileRow = $("<div/>")
|
||||||
.attr("id", 'files' + fileNumber)
|
.attr("id", 'files' + fileNumber)
|
||||||
@ -248,6 +282,13 @@
|
|||||||
.attr("name", "files[" + fileNumber + "].tmpFileName");
|
.attr("name", "files[" + fileNumber + "].tmpFileName");
|
||||||
newFileRow.append(tmpFileNameInput);
|
newFileRow.append(tmpFileNameInput);
|
||||||
|
|
||||||
|
var isLatexInput = $("<input/>")
|
||||||
|
.attr("type", "hidden")
|
||||||
|
.attr("id", "files" + fileNumber + ".isLatexAttach")
|
||||||
|
.attr("value", isLatexAttach)
|
||||||
|
.attr("name", "files[" + fileNumber + "].isLatexAttach");
|
||||||
|
newFileRow.append(isLatexInput);
|
||||||
|
|
||||||
var nextDiv = $("<div/>")
|
var nextDiv = $("<div/>")
|
||||||
.addClass("col-2");
|
.addClass("col-2");
|
||||||
|
|
||||||
@ -266,7 +307,7 @@
|
|||||||
.attr("onclick", "downloadFile('" + fileDto.tmpFileName + "',null,'" + fileDto.fileName + "')"));
|
.attr("onclick", "downloadFile('" + fileDto.tmpFileName + "',null,'" + fileDto.fileName + "')"));
|
||||||
newFileRow.append(nameDiv);
|
newFileRow.append(nameDiv);
|
||||||
|
|
||||||
$("#files-list").append(newFileRow);
|
listElement.append(newFileRow);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,6 +339,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generatePDF() {
|
||||||
|
$('#pdfLoadingIcon').show();
|
||||||
|
$('#pdfBtn').prop('disabled', true);
|
||||||
|
|
||||||
|
var formData = new FormData(document.forms.paperform);
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", urlPdfGenerating);
|
||||||
|
xhr.send(formData);
|
||||||
|
xhr.responseType = 'blob';
|
||||||
|
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (this.status == 200) {
|
||||||
|
console.debug(this.response);
|
||||||
|
var blob = new Blob([this.response], {type: 'application/pdf'});
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.style = "display: none";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
let url = window.URL.createObjectURL(blob);
|
||||||
|
a.href = url;
|
||||||
|
a.download = $('#title').val() + '.pdf';
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} else {
|
||||||
|
showFeedbackMessage("Ошибка при создании PDF", MessageTypesEnum.DANGER);
|
||||||
|
}
|
||||||
|
$('#pdfLoadingIcon').hide();
|
||||||
|
$('#pdfBtn').prop('disabled', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
24
src/main/resources/templates/projects/dashboard.html
Normal file
24
src/main/resources/templates/projects/dashboard.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<section id="services">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading text-uppercase">Проекты</h2>
|
||||||
|
<div th:replace="projects/fragments/projectNavigationFragment"/>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center" id="dashboard">
|
||||||
|
<th:block>
|
||||||
|
<div/>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:fragment="headerfiles">
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div th:fragment="projectLine (project)" class="row text-left project-row" style="background-color: white;">
|
||||||
|
<div class="col">
|
||||||
|
<span th:replace="projects/fragments/projectStatusFragment :: projectStatus(projectStatus=${project.status})"/>
|
||||||
|
<a th:href="@{'project?id='+${project.id}}">
|
||||||
|
<span class="h6" th:text="${project.title}"/>
|
||||||
|
<span class="text-muted" th:text="${project.description}"/>
|
||||||
|
</a>
|
||||||
|
<input class="id-class" type="hidden" th:value="${project.id}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:fragment="headerfiles">
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
|
||||||
|
<a href="./projects" class="btn btn-light toolbar-button"><i class="fa fa-list-alt"></i>
|
||||||
|
Список</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
|
||||||
|
<a href="./dashboard" class="btn btn-light toolbar-button"><i class="fa fa-newspaper-o"
|
||||||
|
aria-hidden="true"></i> Панель
|
||||||
|
управления</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-sm-12 col-md-12 col-lg-4 col-xl-3">
|
||||||
|
<a href="./project?id=0" class="btn btn-light toolbar-button"><i class="fa fa-plus-circle"
|
||||||
|
aria-hidden="true"></i>
|
||||||
|
Добавить проект</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head th:fragment="headerfiles">
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span th:fragment="paperStatus (paperStatus)" class="fa-stack fa-1x">
|
||||||
|
<th:block th:switch="${projectStatus.name()}">
|
||||||
|
<div th:case="'APPLICATION'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-draft"></i>
|
||||||
|
</div>
|
||||||
|
<div th:case="'ON_COMPETITION'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-review"></i>
|
||||||
|
</div>
|
||||||
|
<div th:case="'SUCCESSFUL_PASSAGE'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-accepted"></i>
|
||||||
|
</div>
|
||||||
|
<div th:case="'IN_WORK'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-primary"></i>
|
||||||
|
</div>
|
||||||
|
<div th:case="'COMPLETED'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-success"></i>
|
||||||
|
</div>
|
||||||
|
<div th:case="'FAILED'">
|
||||||
|
<i class="fa fa-circle fa-stack-2x text-failed"></i>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
|
||||||
|
</span>
|
||||||
|
</body>
|
||||||
|
</html>
|
146
src/main/resources/templates/projects/project.html
Normal file
146
src/main/resources/templates/projects/project.html
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorator="default" xmlns:th="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/html">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
|
||||||
|
<section id="paper">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading text-uppercase">Редактирование проекта</h2>
|
||||||
|
<div th:replace="projects/fragments/projectNavigationFragment"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<form id="project-form" method="post"
|
||||||
|
th:action="@{'/projects/project?id='+ *{id == null ? '' : id} + ''}"
|
||||||
|
th:object="${projectDto}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-sm-12">
|
||||||
|
<input type="hidden" name="id" th:field="*{id}"/>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Название:</label>
|
||||||
|
<input class="form-control" id="title" type="text"
|
||||||
|
placeholder="Название проекта"
|
||||||
|
th:field="*{title}"/>
|
||||||
|
<p th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
|
||||||
|
class="alert alert-danger">Incorrect title</p>
|
||||||
|
<p class="help-block text-danger"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="status">Статус:</label>
|
||||||
|
<select class="form-control" th:field="*{status}" id="status">
|
||||||
|
<option th:each="status : ${allStatuses}" th:value="${status}"
|
||||||
|
th:text="${status.statusName}">Status
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description">Описание:</label>
|
||||||
|
<textarea class="form-control" rows="3" id="description"
|
||||||
|
th:field="*{description}"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="createGrant">Добавить грант:</label>
|
||||||
|
<div th:if="*{grant} == null">
|
||||||
|
<input type="submit" id="createGrant" name="createGrant" class="btn btn-primary"
|
||||||
|
value="Добавить грант"/>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" th:field="*{grant.id}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="repository">Ссылка на репозиторий:</label>
|
||||||
|
<input class="form-control" id="repository" type="text"
|
||||||
|
placeholder="http://"
|
||||||
|
th:field="*{repository}"/>
|
||||||
|
<p th:if="${#fields.hasErrors('repository')}" th:errors="*{repository}"
|
||||||
|
class="alert alert-danger">Incorrect repository link</p>
|
||||||
|
<p class="help-block text-danger"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Дедлайны показателей:</label>
|
||||||
|
<div class="row">
|
||||||
|
<input type="hidden"/>
|
||||||
|
<div class="col-6">
|
||||||
|
<input type="date" class="form-control" name="deadline"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input class="form-control" type="text" placeholder="Описание"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<a class="btn btn-danger float-right"><span
|
||||||
|
aria-hidden="true"><i class="fa fa-times"/></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p th:if="${#fields.hasErrors('deadlines')}" th:errors="*{deadlines}"
|
||||||
|
class="alert alert-danger">Incorrect title</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="submit" id="addDeadline" name="addDeadline" class="btn btn-primary"
|
||||||
|
value="Добавить
|
||||||
|
дедлайн"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="loader">Загрузить файл:</label>
|
||||||
|
<div id="loader">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<button id="sendMessageButton" name="save" class="btn btn-success text-uppercase"
|
||||||
|
type="submit">
|
||||||
|
Сохранить
|
||||||
|
</button>
|
||||||
|
<button id="cancelButton" class="btn btn-default text-uppercase"
|
||||||
|
href="/projects/projects">
|
||||||
|
Отмена
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script type="text/javascript" src="/js/file-loader.js"></script>
|
||||||
|
<script>
|
||||||
|
/*<![CDATA[*/
|
||||||
|
$(document).ready(function () {
|
||||||
|
new FileLoader({
|
||||||
|
div: "loader",
|
||||||
|
url: urlFileUpload,
|
||||||
|
maxSize: 2,
|
||||||
|
extensions: ["doc", "docx", "xls", "jpg", "png", "pdf", "txt"],
|
||||||
|
callback: function (response) {
|
||||||
|
showFeedbackMessage("Файл успешно загружен");
|
||||||
|
console.debug(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('.selectpicker').selectpicker();
|
||||||
|
});
|
||||||
|
/*]]>*/
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
33
src/main/resources/templates/projects/projects.html
Normal file
33
src/main/resources/templates/projects/projects.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorator="default" xmlns:th="">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<form id="projects-form" method="post" th:action="@{'/projects/projects'}">
|
||||||
|
<input th:type="hidden" name="projectDeleteId" id="projectDeleteId"/>
|
||||||
|
<section id="projects">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row" id="project-list">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading text-uppercase">Проекты</h2>
|
||||||
|
<div th:replace="projects/fragments/projectNavigationFragment"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9 col-sm-12">
|
||||||
|
<th:block th:each="project : ${projects}">
|
||||||
|
<div th:replace="projects/fragments/projectLineFragment :: projectLine(project=${project})"/>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-12">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -16,7 +16,7 @@
|
|||||||
<div class="row justify-content-center" id="dashboard">
|
<div class="row justify-content-center" id="dashboard">
|
||||||
<div th:replace="students/fragments/taskDashboardFragment"/>
|
<div th:replace="students/fragments/taskDashboardFragment"/>
|
||||||
<!--<th:block th:each="task : ${tasks}">-->
|
<!--<th:block th:each="task : ${tasks}">-->
|
||||||
<!--<div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>-->
|
<!--<div th:replace="students/fragments/taskDashboardFragment :: taskDashboard(task=${task})"/>-->
|
||||||
<!--</th:block>-->
|
<!--</th:block>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
52
src/test/java/IndexPageTest.java
Normal file
52
src/test/java/IndexPageTest.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import core.PageObject;
|
||||||
|
import core.TestTemplate;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import paper.PaperPage;
|
||||||
|
import paper.PapersDashboardPage;
|
||||||
|
import paper.PapersPage;
|
||||||
|
import ru.ulstu.NgTrackerApplication;
|
||||||
|
import ru.ulstu.configuration.ApplicationProperties;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(classes = NgTrackerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||||
|
public class IndexPageTest extends TestTemplate {
|
||||||
|
private final Map<PageObject, List<String>> navigationHolder = ImmutableMap.of(
|
||||||
|
new PapersPage(), Arrays.asList("СТАТЬИ", "/papers/papers"),
|
||||||
|
new PaperPage(), Arrays.asList("РЕДАКТИРОВАНИЕ СТАТЬИ", "/papers/paper?id=0"),
|
||||||
|
new PapersDashboardPage(), Arrays.asList("СТАТЬИ", "/papers/dashboard")
|
||||||
|
);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartApplication() {
|
||||||
|
getContext().goTo(applicationProperties.getBaseUrl());
|
||||||
|
Assertions
|
||||||
|
.assertThat(getContext().getTitle())
|
||||||
|
.isEqualTo("NG-Tracker");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testModulesNavigation() {
|
||||||
|
navigationHolder.entrySet()
|
||||||
|
.stream()
|
||||||
|
.forEach(navigationEntry -> {
|
||||||
|
getContext().goTo(applicationProperties.getBaseUrl() + navigationEntry.getValue().get(1));
|
||||||
|
PageObject pageObject = getContext().initPage(navigationEntry.getKey());
|
||||||
|
Assertions
|
||||||
|
.assertThat(pageObject.getSubTitle())
|
||||||
|
.isEqualToIgnoringCase(navigationEntry.getValue().get(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
27
src/test/java/context/ChromeContext.java
Normal file
27
src/test/java/context/ChromeContext.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package context;
|
||||||
|
|
||||||
|
import org.openqa.selenium.chrome.ChromeDriver;
|
||||||
|
import org.openqa.selenium.chrome.ChromeOptions;
|
||||||
|
|
||||||
|
public class ChromeContext extends Context {
|
||||||
|
private final static String WINDOWS_DRIVER = "chromedriver.exe";
|
||||||
|
private final static String LINUX_DRIVER = "chromedriver";
|
||||||
|
private final static String DRIVER_TYPE = "webdriver.chrome.driver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createDriver() {
|
||||||
|
final ChromeOptions chromeOptions = new ChromeOptions();
|
||||||
|
chromeOptions.addArguments("--headless");
|
||||||
|
driver = new ChromeDriver(chromeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDriverExecutable(boolean isWindows) {
|
||||||
|
return isWindows ? WINDOWS_DRIVER : LINUX_DRIVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDriverType() {
|
||||||
|
return DRIVER_TYPE;
|
||||||
|
}
|
||||||
|
}
|
67
src/test/java/context/Context.java
Normal file
67
src/test/java/context/Context.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package context;
|
||||||
|
|
||||||
|
import core.PageObject;
|
||||||
|
import org.openqa.selenium.Dimension;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
//import org.openqa.selenium.support.PageFactory;
|
||||||
|
|
||||||
|
public abstract class Context {
|
||||||
|
private final static String DRIVER_LOCATION = "drivers/%s";
|
||||||
|
|
||||||
|
protected WebDriver driver;
|
||||||
|
|
||||||
|
protected WebDriver getDriver() {
|
||||||
|
if (driver != null) {
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("WebDriver is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
System.setProperty(getDriverType(), getDriverExecutablePath());
|
||||||
|
|
||||||
|
createDriver();
|
||||||
|
// это плохая инструкция для автотестов, т.к. лучше задавать для конкретного элемента или кейса
|
||||||
|
getDriver().manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (driver != null) {
|
||||||
|
driver.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void goTo(String url) {
|
||||||
|
getDriver().get(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return getDriver().getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends PageObject> T initPage(T pageObject) {
|
||||||
|
return (T) pageObject.setDriver(getDriver());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void createDriver();
|
||||||
|
|
||||||
|
protected abstract String getDriverType();
|
||||||
|
|
||||||
|
protected abstract String getDriverExecutable(boolean windows);
|
||||||
|
|
||||||
|
private String getDriverExecutablePath() {
|
||||||
|
return Context.class.getClassLoader().getResource(
|
||||||
|
String.format(DRIVER_LOCATION, getDriverExecutable(isWindows()))).getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWindows() {
|
||||||
|
return System.getProperty("os.name").toLowerCase().contains("windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(Dimension dimension) {
|
||||||
|
driver.manage().window().setSize(dimension);
|
||||||
|
}
|
||||||
|
}
|
24
src/test/java/context/FirefoxContext.java
Normal file
24
src/test/java/context/FirefoxContext.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package context;
|
||||||
|
|
||||||
|
import org.openqa.selenium.firefox.FirefoxDriver;
|
||||||
|
|
||||||
|
public class FirefoxContext extends Context {
|
||||||
|
private final static String WINDOWS_DRIVER = "geckodriver.exe";
|
||||||
|
private final static String LINUX_DRIVER = "geckodriver";
|
||||||
|
private final static String DRIVER_TYPE = "webdriver.gecko.driver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createDriver() {
|
||||||
|
driver = new FirefoxDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDriverExecutable(boolean isWindows) {
|
||||||
|
return isWindows ? WINDOWS_DRIVER : LINUX_DRIVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDriverType() {
|
||||||
|
return DRIVER_TYPE;
|
||||||
|
}
|
||||||
|
}
|
14
src/test/java/core/PageObject.java
Normal file
14
src/test/java/core/PageObject.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package core;
|
||||||
|
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
public abstract class PageObject {
|
||||||
|
protected WebDriver driver;
|
||||||
|
|
||||||
|
public abstract String getSubTitle();
|
||||||
|
|
||||||
|
public PageObject setDriver(WebDriver driver) {
|
||||||
|
this.driver = driver;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
29
src/test/java/core/TestTemplate.java
Normal file
29
src/test/java/core/TestTemplate.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package core;
|
||||||
|
|
||||||
|
import context.ChromeContext;
|
||||||
|
import context.Context;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.openqa.selenium.Dimension;
|
||||||
|
|
||||||
|
public abstract class TestTemplate {
|
||||||
|
private static Context context;
|
||||||
|
|
||||||
|
public static Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setup() {
|
||||||
|
context = new ChromeContext();
|
||||||
|
context.start();
|
||||||
|
context.setSize(new Dimension(1600, 900));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void quit() {
|
||||||
|
if (context != null) {
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/test/java/paper/PaperPage.java
Normal file
11
src/test/java/paper/PaperPage.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package paper;
|
||||||
|
|
||||||
|
import core.PageObject;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
public class PaperPage extends PageObject {
|
||||||
|
|
||||||
|
public String getSubTitle() {
|
||||||
|
return driver.findElement(By.tagName("h2")).getText();
|
||||||
|
}
|
||||||
|
}
|
11
src/test/java/paper/PapersDashboardPage.java
Normal file
11
src/test/java/paper/PapersDashboardPage.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package paper;
|
||||||
|
|
||||||
|
import core.PageObject;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
public class PapersDashboardPage extends PageObject {
|
||||||
|
|
||||||
|
public String getSubTitle() {
|
||||||
|
return driver.findElement(By.tagName("h2")).getText();
|
||||||
|
}
|
||||||
|
}
|
11
src/test/java/paper/PapersPage.java
Normal file
11
src/test/java/paper/PapersPage.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package paper;
|
||||||
|
|
||||||
|
import core.PageObject;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
public class PapersPage extends PageObject {
|
||||||
|
|
||||||
|
public String getSubTitle() {
|
||||||
|
return driver.findElement(By.tagName("h2")).getText();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user