21-rating #22
@ -33,8 +33,8 @@ dependencies {
|
|||||||
implementation group: 'com.h2database', name:'h2'
|
implementation group: 'com.h2database', name:'h2'
|
||||||
implementation group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.2'
|
implementation group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.2'
|
||||||
implementation group: 'org.javassist', name: 'javassist', version: '3.30.2-GA'
|
implementation group: 'org.javassist', name: 'javassist', version: '3.30.2-GA'
|
||||||
implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-blackbird'
|
|
||||||
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.13.2'
|
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.8.4'
|
||||||
|
|
||||||
implementation group: 'org.eclipse.jetty', name: 'jetty-servlets', version: '11.0.24'
|
implementation group: 'org.eclipse.jetty', name: 'jetty-servlets', version: '11.0.24'
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ import ru.ulstu.aspirant.service.AspirantService;
|
|||||||
import ru.ulstu.manager.model.Manager;
|
import ru.ulstu.manager.model.Manager;
|
||||||
import ru.ulstu.manager.service.ManagerService;
|
import ru.ulstu.manager.service.ManagerService;
|
||||||
import ru.ulstu.model.User;
|
import ru.ulstu.model.User;
|
||||||
|
import ru.ulstu.model.UserRole;
|
||||||
|
import ru.ulstu.model.UserRoleConstants;
|
||||||
|
import ru.ulstu.user.UserRoleRepository;
|
||||||
import ru.ulstu.user.UserService;
|
import ru.ulstu.user.UserService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -16,13 +19,16 @@ public class AdminAspirantService {
|
|||||||
private final AspirantService aspirantService;
|
private final AspirantService aspirantService;
|
||||||
private final ManagerService managerService;
|
private final ManagerService managerService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
|
||||||
public AdminAspirantService(AspirantService aspirantService,
|
public AdminAspirantService(AspirantService aspirantService,
|
||||||
ManagerService managerService,
|
ManagerService managerService,
|
||||||
UserService userService) {
|
UserService userService,
|
||||||
|
UserRoleRepository userRoleRepository) {
|
||||||
this.aspirantService = aspirantService;
|
this.aspirantService = aspirantService;
|
||||||
this.managerService = managerService;
|
this.managerService = managerService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
|
this.userRoleRepository = userRoleRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Aspirant> getAspirants() {
|
public List<Aspirant> getAspirants() {
|
||||||
@ -46,10 +52,18 @@ public class AdminAspirantService {
|
|||||||
aspirant.setBirthDate(aspirantForm.getBirthDate());
|
aspirant.setBirthDate(aspirantForm.getBirthDate());
|
||||||
aspirant.setSpeciality(aspirantForm.getSpeciality());
|
aspirant.setSpeciality(aspirantForm.getSpeciality());
|
||||||
aspirant.setTheme(aspirantForm.getTheme());
|
aspirant.setTheme(aspirantForm.getTheme());
|
||||||
User user = userService.getUserById(aspirantForm.getUserId());
|
User user;
|
||||||
if (!user.getLogin().equals(aspirantForm.getEmail())) {
|
if (aspirantForm.getUserId() != null) {
|
||||||
user.setLogin(aspirantForm.getEmail());
|
user = userService.getUserById(aspirantForm.getUserId());
|
||||||
userService.createUser(user);
|
if (!user.getLogin().equals(aspirantForm.getEmail())) {
|
||||||
|
user.setLogin(aspirantForm.getEmail());
|
||||||
|
if (user.getRoles().stream().anyMatch(r -> !r.getName().equals(UserRoleConstants.ASPIRANT))) {
|
||||||
|
user.getRoles().add(userRoleRepository.save(new UserRole(UserRoleConstants.ASPIRANT)));
|
||||||
|
}
|
||||||
|
userService.createUser(user);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user = userService.createAspirant(aspirantForm.getEmail());
|
||||||
}
|
}
|
||||||
aspirant.setUser(user);
|
aspirant.setUser(user);
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class AspirantService {
|
|||||||
return aspirantRepository.findByUser(user);
|
return aspirantRepository.findByUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Indicator> getIndicatorsByCourse() {
|
public List<Indicator> getCurrentAspirantIndicators() {
|
||||||
Aspirant aspirant = getAspirantByUser(userService.getCurrentUser());
|
Aspirant aspirant = getAspirantByUser(userService.getCurrentUser());
|
||||||
return indicatorService.getIndicatorsByCourse(aspirant.getCourse());
|
return indicatorService.getIndicatorsByCourse(aspirant.getCourse());
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ public class SecurityConfiguration {
|
|||||||
log.debug("Security enabled");
|
log.debug("Security enabled");
|
||||||
|
|
||||||
http
|
http
|
||||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
|
||||||
.csrf(AbstractHttpConfigurer::disable)
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
.authorizeHttpRequests(auth ->
|
.authorizeHttpRequests(auth ->
|
||||||
auth.requestMatchers("/").permitAll()
|
auth.requestMatchers("/").permitAll()
|
||||||
.requestMatchers(permittedUrls).permitAll()
|
.requestMatchers(permittedUrls).permitAll()
|
||||||
.requestMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
.requestMatchers("/swagger-ui/*").hasAuthority(UserRoleConstants.ADMIN)
|
||||||
.anyRequest().authenticated())
|
.anyRequest().authenticated())
|
||||||
.formLogin(form ->
|
.formLogin(form ->
|
||||||
form.loginPage("/login")
|
form.loginPage("/login")
|
||||||
|
54
src/main/java/ru/ulstu/file/controller/FileController.java
Normal file
54
src/main/java/ru/ulstu/file/controller/FileController.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package ru.ulstu.file.controller;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import ru.ulstu.file.model.FileData;
|
||||||
|
import ru.ulstu.file.model.FileDataDto;
|
||||||
|
import ru.ulstu.file.service.FileService;
|
||||||
|
import ru.ulstu.model.response.Response;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("files")
|
||||||
|
public class FileController {
|
||||||
|
private final FileService fileService;
|
||||||
|
|
||||||
|
public FileController(FileService fileService) {
|
||||||
|
this.fileService = fileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/download-tmp/{tmp-file-name}")
|
||||||
|
public ResponseEntity<byte[]> getFile(@PathVariable("tmp-file-name") String tmpFileName) throws IOException {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Content-Disposition", "attachment; filename='"
|
||||||
|
+ URLEncoder.encode(fileService.getTmpFileName(tmpFileName), UTF_8.toString()) + "'");
|
||||||
|
return new ResponseEntity<>(fileService.getTmpFile(tmpFileName), headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/download/{file-id}")
|
||||||
|
public ResponseEntity<byte[]> getFile(@PathVariable("file-id") Integer fileId) throws UnsupportedEncodingException {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
FileData fileData = fileService.getFile(fileId);
|
||||||
|
headers.add("Content-Disposition", "attachment; filename='" +
|
||||||
|
URLEncoder.encode(fileData.getName(), UTF_8.toString()) + "'");
|
||||||
|
return new ResponseEntity<>(fileData.getData(), headers, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/uploadTmpFile")
|
||||||
|
public Response<FileDataDto> upload(@RequestParam("file") MultipartFile multipartFile) throws IOException {
|
||||||
|
return new Response<>(fileService.createFromMultipartFile(multipartFile));
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ 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() {
|
||||||
}
|
}
|
||||||
@ -17,20 +16,17 @@ 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) {
|
||||||
@ -77,20 +73,4 @@ public class FileDataDto {
|
|||||||
public void setDeleted(boolean deleted) {
|
public void setDeleted(boolean deleted) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/main/java/ru/ulstu/model/ErrorConstants.java
Normal file
39
src/main/java/ru/ulstu/model/ErrorConstants.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package ru.ulstu.model;
|
||||||
|
|
||||||
|
public enum ErrorConstants {
|
||||||
|
UNKNOWN(0, "Unknown error"),
|
||||||
|
ID_IS_NULL(1, "Id of entity has null value"),
|
||||||
|
VALIDATION_ERROR(2, "Validation error"),
|
||||||
|
USER_ID_EXISTS(100, "New user can't have id"),
|
||||||
|
USER_ACTIVATION_ERROR(101, "Invalid activation key"),
|
||||||
|
USER_EMAIL_EXISTS(102, "Пользователь с таким почтовым ящиком уже существует"),
|
||||||
|
USER_LOGIN_EXISTS(103, "Пользователь с таким логином уже существует"),
|
||||||
|
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "Пароли введены неверно"),
|
||||||
|
USER_NOT_FOUND(105, "Аккаунт не найден"),
|
||||||
|
USER_NOT_ACTIVATED(106, "User is not activated"),
|
||||||
|
USER_RESET_ERROR(107, "Некорректный ключ подтверждения"),
|
||||||
|
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
|
||||||
|
FILE_UPLOAD_ERROR(110, "File upload error"),
|
||||||
|
USER_SENDING_MAIL_EXCEPTION(111, "Во время отправки приглашения пользователю произошла ошибка");
|
||||||
|
|
||||||
|
private final int code;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
ErrorConstants(int code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%d: %s", code, message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.ulstu.model.response;
|
||||||
|
|
||||||
|
class ControllerResponse<D, E> {
|
||||||
|
private final D data;
|
||||||
|
private final ControllerResponseError<E> error;
|
||||||
|
|
||||||
|
ControllerResponse(D data) {
|
||||||
|
this.data = data;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerResponse(ControllerResponseError<E> error) {
|
||||||
|
this.data = null;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public D getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ControllerResponseError<E> getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.ulstu.model.response;
|
||||||
|
|
||||||
|
|
||||||
|
import ru.ulstu.model.ErrorConstants;
|
||||||
|
|
||||||
|
class ControllerResponseError<D> {
|
||||||
|
private final ErrorConstants description;
|
||||||
|
private final D data;
|
||||||
|
|
||||||
|
ControllerResponseError(ErrorConstants description, D data) {
|
||||||
|
this.description = description;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return description.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return description.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public D getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/ru/ulstu/model/response/PageableItems.java
Normal file
21
src/main/java/ru/ulstu/model/response/PageableItems.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ru.ulstu.model.response;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class PageableItems<T> {
|
||||||
|
private final long count;
|
||||||
|
private final Collection<T> items;
|
||||||
|
|
||||||
|
public PageableItems(long count, Collection<T> items) {
|
||||||
|
this.count = count;
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<T> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
17
src/main/java/ru/ulstu/model/response/Response.java
Normal file
17
src/main/java/ru/ulstu/model/response/Response.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package ru.ulstu.model.response;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import ru.ulstu.model.ErrorConstants;
|
||||||
|
|
||||||
|
public class Response<D> extends ResponseEntity<Object> {
|
||||||
|
|
||||||
|
public Response(D data) {
|
||||||
|
super(new ControllerResponse<D, Void>(data), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response(ErrorConstants error) {
|
||||||
|
super(new ControllerResponse<Void, Void>(new ControllerResponseError<>(error, null)), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/ulstu/model/response/ResponseExtended.java
Normal file
13
src/main/java/ru/ulstu/model/response/ResponseExtended.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.model.response;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import ru.ulstu.model.ErrorConstants;
|
||||||
|
|
||||||
|
|
||||||
|
public class ResponseExtended<E> extends ResponseEntity<Object> {
|
||||||
|
|
||||||
|
public ResponseExtended(ErrorConstants error, E errorData) {
|
||||||
|
super(new ControllerResponse<Void, E>(new ControllerResponseError<E>(error, errorData)), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,35 @@
|
|||||||
package ru.ulstu.report.controller;
|
package ru.ulstu.report.controller;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import ru.ulstu.aspirant.service.AspirantService;
|
import ru.ulstu.aspirant.service.AspirantService;
|
||||||
import ru.ulstu.indicator.model.Indicator;
|
import ru.ulstu.indicator.model.Indicator;
|
||||||
import ru.ulstu.model.OffsetablePageRequest;
|
import ru.ulstu.indicator.service.IndicatorService;
|
||||||
import ru.ulstu.report.model.Report;
|
|
||||||
import ru.ulstu.report.model.ReportListForm;
|
import ru.ulstu.report.model.ReportListForm;
|
||||||
import ru.ulstu.report.model.ReportPeriod;
|
import ru.ulstu.report.model.ReportPeriod;
|
||||||
|
import ru.ulstu.report.model.dto.ReportDto;
|
||||||
import ru.ulstu.report.service.ReportPeriodService;
|
import ru.ulstu.report.service.ReportPeriodService;
|
||||||
import ru.ulstu.report.service.ReportService;
|
import ru.ulstu.report.service.ReportService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("report")
|
@RequestMapping("report")
|
||||||
public class ReportController {
|
public class ReportController {
|
||||||
private static final Integer DEFAULT_PAGE_SIZE = 1;
|
private final IndicatorService indicatorService;
|
||||||
private static final Integer DEFAULT_PAGE_NUMBER = 1;
|
|
||||||
|
|
||||||
private final AspirantService aspirantService;
|
private final AspirantService aspirantService;
|
||||||
private final ReportService reportService;
|
private final ReportService reportService;
|
||||||
private final ReportPeriodService reportPeriodService;
|
private final ReportPeriodService reportPeriodService;
|
||||||
|
|
||||||
public ReportController(AspirantService aspirantService,
|
public ReportController(IndicatorService indicatorService,
|
||||||
|
AspirantService aspirantService,
|
||||||
ReportService reportService,
|
ReportService reportService,
|
||||||
ReportPeriodService reportPeriodService) {
|
ReportPeriodService reportPeriodService) {
|
||||||
|
this.indicatorService = indicatorService;
|
||||||
this.aspirantService = aspirantService;
|
this.aspirantService = aspirantService;
|
||||||
this.reportService = reportService;
|
this.reportService = reportService;
|
||||||
this.reportPeriodService = reportPeriodService;
|
this.reportPeriodService = reportPeriodService;
|
||||||
@ -42,9 +37,9 @@ public class ReportController {
|
|||||||
|
|
||||||
@GetMapping("reportList")
|
@GetMapping("reportList")
|
||||||
public String getReportPeriods(Model model) {
|
public String getReportPeriods(Model model) {
|
||||||
model.addAttribute("reportListForm", new ReportListForm());
|
|
||||||
List<ReportPeriod> periods = reportPeriodService.getReportPeriods();
|
List<ReportPeriod> periods = reportPeriodService.getReportPeriods();
|
||||||
model.addAttribute("reportPeriods", periods);
|
model.addAttribute("reportPeriods", periods);
|
||||||
|
model.addAttribute("reportListForm", new ReportListForm(periods.isEmpty() ? null : periods.getFirst()));
|
||||||
model.addAttribute("canCreate", !periods.isEmpty() && reportService.canCreateReport(periods.getFirst()));
|
model.addAttribute("canCreate", !periods.isEmpty() && reportService.canCreateReport(periods.getFirst()));
|
||||||
return "report/reportList";
|
return "report/reportList";
|
||||||
}
|
}
|
||||||
@ -59,25 +54,12 @@ public class ReportController {
|
|||||||
return "report/reportList";
|
return "report/reportList";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("editReport/{reportId}")
|
@GetMapping("editReport/{reportId}/{reportPeriodId}")
|
||||||
public String createReport(@PathVariable("reportId") Integer reportId,
|
public String createReport(@PathVariable("reportId") Integer reportId,
|
||||||
Model model,
|
@PathVariable("reportPeriodId") Integer reportPeriodId,
|
||||||
@RequestParam Optional<Integer> page,
|
Model model) {
|
||||||
@RequestParam Optional<Integer> size) {
|
List<Indicator> indicators = aspirantService.getCurrentAspirantIndicators();
|
||||||
int currentPage = page.orElse(DEFAULT_PAGE_NUMBER);
|
model.addAttribute("report", new ReportDto(reportId, reportPeriodId, indicators));
|
||||||
int pageSize = size.orElse(DEFAULT_PAGE_SIZE);
|
|
||||||
|
|
||||||
Page<Indicator> indicators = aspirantService.getIndicatorsByCourse(new OffsetablePageRequest(currentPage - 1, pageSize));
|
|
||||||
model.addAttribute("indicators", indicators);
|
|
||||||
int totalPages = indicators.getTotalPages();
|
|
||||||
if (totalPages > 0) {
|
|
||||||
List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
|
||||||
.boxed()
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
model.addAttribute("pageNumbers", pageNumbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
model.addAttribute("report", new Report(0));
|
|
||||||
return "report/editReport";
|
return "report/editReport";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ public class Report extends BaseEntity {
|
|||||||
@ManyToOne
|
@ManyToOne
|
||||||
private Aspirant aspirant;
|
private Aspirant aspirant;
|
||||||
|
|
||||||
|
public Report() {
|
||||||
|
}
|
||||||
|
|
||||||
public Report(Integer id) {
|
public Report(Integer id) {
|
||||||
setId(id);
|
setId(id);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,13 @@ package ru.ulstu.report.model;
|
|||||||
public class ReportListForm {
|
public class ReportListForm {
|
||||||
private ReportPeriod reportPeriod;
|
private ReportPeriod reportPeriod;
|
||||||
|
|
||||||
|
public ReportListForm() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReportListForm(ReportPeriod reportPeriod) {
|
||||||
|
this.reportPeriod = reportPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
public ReportPeriod getReportPeriod() {
|
public ReportPeriod getReportPeriod() {
|
||||||
return reportPeriod;
|
return reportPeriod;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
package ru.ulstu.report.model;
|
package ru.ulstu.report.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import org.hibernate.annotations.Fetch;
|
||||||
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
import ru.ulstu.file.model.FileData;
|
||||||
import ru.ulstu.indicator.model.Indicator;
|
import ru.ulstu.indicator.model.Indicator;
|
||||||
import ru.ulstu.model.BaseEntity;
|
import ru.ulstu.model.BaseEntity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class ReportValue extends BaseEntity {
|
public class ReportValue extends BaseEntity {
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
private Indicator indicator;
|
private Indicator indicator;
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "report_value_id", unique = true)
|
||||||
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
|
private List<FileData> files = new ArrayList<>();
|
||||||
private int indicatorValue;
|
private int indicatorValue;
|
||||||
|
|
||||||
public Indicator getIndicator() {
|
public Indicator getIndicator() {
|
||||||
@ -27,4 +40,12 @@ public class ReportValue extends BaseEntity {
|
|||||||
public void setIndicatorValue(int indicatorValue) {
|
public void setIndicatorValue(int indicatorValue) {
|
||||||
this.indicatorValue = indicatorValue;
|
this.indicatorValue = indicatorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<FileData> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFiles(List<FileData> files) {
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
52
src/main/java/ru/ulstu/report/model/dto/ReportDto.java
Normal file
52
src/main/java/ru/ulstu/report/model/dto/ReportDto.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package ru.ulstu.report.model.dto;
|
||||||
|
|
||||||
|
import ru.ulstu.indicator.model.Indicator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReportDto {
|
||||||
|
private Integer id;
|
||||||
|
private Integer reportPeriodId;
|
||||||
|
private Date createDate = new Date();
|
||||||
|
private List<ReportValueDto> reportValues = new ArrayList<>();
|
||||||
|
|
||||||
|
public ReportDto(Integer id, Integer reportPeriodId, List<Indicator> indicators) {
|
||||||
|
this.id = id;
|
||||||
|
this.reportPeriodId = reportPeriodId;
|
||||||
|
this.reportValues = indicators.stream().map(ReportValueDto::new).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ReportValueDto> getReportValues() {
|
||||||
|
return reportValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReportValues(List<ReportValueDto> reportValues) {
|
||||||
|
this.reportValues = reportValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getReportPeriodId() {
|
||||||
|
return reportPeriodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReportPeriodId(Integer reportPeriodId) {
|
||||||
|
this.reportPeriodId = reportPeriodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreateDate() {
|
||||||
|
return createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateDate(Date createDate) {
|
||||||
|
this.createDate = createDate;
|
||||||
|
}
|
||||||
|
}
|
44
src/main/java/ru/ulstu/report/model/dto/ReportValueDto.java
Normal file
44
src/main/java/ru/ulstu/report/model/dto/ReportValueDto.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package ru.ulstu.report.model.dto;
|
||||||
|
|
||||||
|
import ru.ulstu.file.model.FileDataDto;
|
||||||
|
import ru.ulstu.indicator.model.Indicator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReportValueDto {
|
||||||
|
private Indicator indicator;
|
||||||
|
private int indicatorValue;
|
||||||
|
private List<FileDataDto> files = new ArrayList<>();
|
||||||
|
|
||||||
|
public ReportValueDto() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReportValueDto(Indicator indicator) {
|
||||||
|
this.indicator = indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Indicator getIndicator() {
|
||||||
|
return indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicator(Indicator indicator) {
|
||||||
|
this.indicator = indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndicatorValue() {
|
||||||
|
return indicatorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicatorValue(int indicatorValue) {
|
||||||
|
this.indicatorValue = indicatorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FileDataDto> getFiles() {
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFiles(List<FileDataDto> files) {
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
|
}
|
@ -72,11 +72,16 @@ public class UserService implements UserDetailsService {
|
|||||||
return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found by id"));
|
return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found by id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDefaultUser(String login, String userRole) {
|
private User createDefaultUser(String login, String userRole) {
|
||||||
if (getUserByLogin(login) == null) {
|
if (getUserByLogin(login) == null) {
|
||||||
UserRole role = userRoleRepository.save(new UserRole(userRole.toString()));
|
UserRole role = userRoleRepository.save(new UserRole(userRole.toString()));
|
||||||
createUser(new User(login, login.equals("admin") ? adminPassword : login, Set.of(role)));
|
return createUser(new User(login, login.equals("admin") ? adminPassword : login, Set.of(role)));
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User createAspirant(String login) {
|
||||||
|
return createDefaultUser(login, UserRoleConstants.ASPIRANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initDefaultAdmin() {
|
public void initDefaultAdmin() {
|
||||||
|
236
src/main/resources/public/js/core.js
Normal file
236
src/main/resources/public/js/core.js
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
// from config.js
|
||||||
|
/* global urlVersions */
|
||||||
|
|
||||||
|
var urlFileUpload = "/files/uploadTmpFile";
|
||||||
|
var urlFileDownload = "/files/download/";
|
||||||
|
var urlFileDownloadTmp = "/files/download-tmp/";
|
||||||
|
|
||||||
|
/* exported MessageTypesEnum */
|
||||||
|
var MessageTypesEnum = {
|
||||||
|
INFO: "info",
|
||||||
|
SUCCESS: "success",
|
||||||
|
WARNING: "warning",
|
||||||
|
DANGER: "danger"
|
||||||
|
};
|
||||||
|
Object.freeze(MessageTypesEnum);
|
||||||
|
|
||||||
|
function isEmpty(value) {
|
||||||
|
if (typeof value === "function") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (value == null || value.length === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported showFeedbackMessage */
|
||||||
|
function showFeedbackMessage(message, type) {
|
||||||
|
var drawMessage = function (message, type) {
|
||||||
|
var li = $(
|
||||||
|
"<li class=\"alert alert-" + type + "\">" +
|
||||||
|
"<span>" + message + "</span>" +
|
||||||
|
"</li>"
|
||||||
|
);
|
||||||
|
li.click(function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
messageDiv.append(li);
|
||||||
|
};
|
||||||
|
|
||||||
|
var liveTimeMs = 15000;
|
||||||
|
var messageDiv = $("#messages");
|
||||||
|
if (isEmpty(message)) {
|
||||||
|
messageDiv.html("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drawMessage(message, (isEmpty(type) || isEmpty(MessageTypesEnum[type.toUpperCase()]) ?
|
||||||
|
MessageTypesEnum.INFO : type));
|
||||||
|
setTimeout(function () {
|
||||||
|
messageDiv.find("li").first().remove();
|
||||||
|
}, liveTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported errorHandler */
|
||||||
|
function errorHandler(response, callBack, errorCallBack) {
|
||||||
|
if (isEmpty(response)) {
|
||||||
|
showFeedbackMessage("Empty response", MessageTypesEnum.DANGER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isEmpty(response.error)) {
|
||||||
|
// TODO: add l10n
|
||||||
|
// showFeedbackMessage(response.error.code + ": " + response.error.message, MessageTypesEnum.DANGER);
|
||||||
|
if (!isEmpty(errorCallBack)) {
|
||||||
|
errorCallBack(response.data);
|
||||||
|
}
|
||||||
|
throw response.error.code + ": " + response.error.message +
|
||||||
|
" / Details: " + response.error.data;
|
||||||
|
}
|
||||||
|
if (!isEmpty(callBack)) {
|
||||||
|
if (isLoginPageResponse(response)) {
|
||||||
|
window.location.href = "/";
|
||||||
|
} else {
|
||||||
|
callBack(response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLoginPageResponse(response) {
|
||||||
|
return (typeof response) === 'string' && response.indexOf("<!DOCTYPE html>") >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported getFromRest */
|
||||||
|
function getFromRest(url, callBack, errorCallBack) {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
cache: false,
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported getFromRestWithVersionAndParams */
|
||||||
|
function getFromRestWithParams(url, params, callBack, errorCallBack) {
|
||||||
|
$.ajax({
|
||||||
|
url: url + "?" + params,
|
||||||
|
cache: false,
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported postToRest */
|
||||||
|
function postToRest(url, postData, callBack, completeCallback, errorCallBack) {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
cache: false,
|
||||||
|
dataType: "json",
|
||||||
|
data: postData,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
processData: false,
|
||||||
|
method: "POST",
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
if (isEmpty(completeCallback)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completeCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported postToRestWithVersion */
|
||||||
|
function postToRestWithVersion(url, postData, callBack, errorCallBack) {
|
||||||
|
postToRestWithVersionAndParams(url, postData, "", callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported postToRestWithVersionAndParams */
|
||||||
|
function postToRestWithVersionAndParams(url, postData, params, callBack, errorCallBack) {
|
||||||
|
getCurrentVersion(function (version) {
|
||||||
|
$.ajax({
|
||||||
|
url: url + "?versionId=" + version + params,
|
||||||
|
cache: false,
|
||||||
|
dataType: "json",
|
||||||
|
data: postData,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
processData: false,
|
||||||
|
method: "POST",
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported putToRestWithVersion */
|
||||||
|
function putToRestWithVersion(url, putData, callBack, errorCallBack) {
|
||||||
|
putToRestWithVersionAndParams(url, putData, "", callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported putToRestWithVersionAndParams */
|
||||||
|
function putToRestWithVersionAndParams(url, putData, params, callBack, errorCallBack) {
|
||||||
|
getCurrentVersion(function (version) {
|
||||||
|
$.ajax({
|
||||||
|
url: url + "?versionId=" + version + params,
|
||||||
|
cache: false,
|
||||||
|
dataType: "json",
|
||||||
|
data: putData,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
processData: false,
|
||||||
|
method: "PUT",
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported deleteFromRest */
|
||||||
|
function deleteFromRest(url, callBack, completeCallback, errorCallBack) {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
cache: false,
|
||||||
|
method: "DELETE",
|
||||||
|
success: function (response) {
|
||||||
|
errorHandler(response, callBack, errorCallBack);
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
if (isEmpty(completeCallback)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completeCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported fillSelect */
|
||||||
|
function fillSelect(selectElement, values) {
|
||||||
|
$(selectElement).html("");
|
||||||
|
$.each(values, function (key, value) {
|
||||||
|
$(selectElement).append(
|
||||||
|
"<option value=\"" + value.id + "\">" + value.name + "</option>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported isUrlVarExists */
|
||||||
|
function isUrlVarExists(key) {
|
||||||
|
var URL = window.location.href;
|
||||||
|
if (URL.indexOf("?" + key + "=") !== -1) {
|
||||||
|
return true;
|
||||||
|
} else if (URL.indexOf("&" + key + "=") !== -1) {
|
||||||
|
return true;
|
||||||
|
} else if (URL.indexOf("?" + key + "&") !== -1) {
|
||||||
|
return true;
|
||||||
|
} else if (URL.indexOf("&" + key + "&") !== -1) {
|
||||||
|
return true;
|
||||||
|
} else if (URL.endsWith("?" + key)) {
|
||||||
|
return true;
|
||||||
|
} else if (URL.endsWith("&" + key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exported getUrlVar */
|
||||||
|
function getUrlVar(key) {
|
||||||
|
var result = new RegExp(key + "=([^&]*)", "i").exec(window.location.search);
|
||||||
|
return result && decodeURIComponent(result[1]) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPing(field, url) {
|
||||||
|
id = document.getElementById(field).value
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url + `?${field}=` + id,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
method: "POST",
|
||||||
|
success: function () {
|
||||||
|
showFeedbackMessage("Ping был отправлен", MessageTypesEnum.SUCCESS)
|
||||||
|
},
|
||||||
|
error: function (errorData) {
|
||||||
|
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
134
src/main/resources/public/js/file-loader.js
Normal file
134
src/main/resources/public/js/file-loader.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// form core.js
|
||||||
|
/* global isEmpty, errorHandler, showFeedbackMessage, MessageTypesEnum */
|
||||||
|
|
||||||
|
/* exported FileLoader */
|
||||||
|
function FileLoader(args) {
|
||||||
|
var MAX_FILE_SIZE_MB = 20;
|
||||||
|
var SIZE_TO_MB = 1048576;
|
||||||
|
var CHOOSE_FILE_TEXT = "Выберите файл";
|
||||||
|
var ALERT_CHOOSE_FILE = "Необходимо выбрать файл";
|
||||||
|
var ALERT_UNKNOWN_FILE_EXT = "Неизвестный тип файлов";
|
||||||
|
var ALERT_MAX_FILE = "Файл превышает разрешенный размер";
|
||||||
|
var ALERT_EMPTY_FILE = "Файл пуст";
|
||||||
|
var ERROR = "Ошибка загрузки файла";
|
||||||
|
|
||||||
|
if (isEmpty(args)) {
|
||||||
|
throw "Empty arguments";
|
||||||
|
}
|
||||||
|
var divId = args.div;
|
||||||
|
if (isEmpty(divId)) {
|
||||||
|
throw "Div id parameter is not set";
|
||||||
|
}
|
||||||
|
var url = args.url;
|
||||||
|
if (isEmpty(url)) {
|
||||||
|
throw "URL parameter is not set";
|
||||||
|
}
|
||||||
|
var callback = args.callback;
|
||||||
|
var maxFileSize = args.maxSize;
|
||||||
|
MAX_FILE_SIZE_MB = Math.min((isEmpty(maxFileSize) ? MAX_FILE_SIZE_MB : maxFileSize), MAX_FILE_SIZE_MB);
|
||||||
|
var fileExtensions = args.extensions;
|
||||||
|
fileExtensions = isEmpty(fileExtensions) ? [] : fileExtensions;
|
||||||
|
|
||||||
|
var div = $("#" + divId).addClass("input-group");
|
||||||
|
if (isEmpty(div)) {
|
||||||
|
throw "Div with id " + divId + " is not found";
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileLabel = $("<input>")
|
||||||
|
.attr("type", "text")
|
||||||
|
.attr("placeholder", CHOOSE_FILE_TEXT)
|
||||||
|
.attr("disabled", true)
|
||||||
|
.addClass("form-control");
|
||||||
|
div.append(fileLabel);
|
||||||
|
var fileInput = $("<input>")
|
||||||
|
.attr("type", "file")
|
||||||
|
.attr("multiple", '')
|
||||||
|
.hide();
|
||||||
|
fileInput.change(function () {
|
||||||
|
var files = $(this).prop("files");
|
||||||
|
if (isEmpty(files)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileLabel.val(files[0].name);
|
||||||
|
});
|
||||||
|
div.append(fileInput);
|
||||||
|
|
||||||
|
var buttonGroup = $("<div>")
|
||||||
|
.addClass("input-group-btn");
|
||||||
|
div.append(buttonGroup);
|
||||||
|
|
||||||
|
var chooseButton = $("<button>")
|
||||||
|
.attr("type", "button")
|
||||||
|
.addClass("btn btn-default")
|
||||||
|
.append($("<i>").addClass("fa fa-ellipsis-h"));
|
||||||
|
chooseButton.click(function () {
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
buttonGroup.append(chooseButton);
|
||||||
|
|
||||||
|
var uploadButton = $("<button>")
|
||||||
|
.attr("type", "button")
|
||||||
|
.addClass("btn btn-default")
|
||||||
|
.append($("<i>").addClass("fa fa-upload"));
|
||||||
|
uploadButton.click(function () {
|
||||||
|
var files = fileInput.prop("files");
|
||||||
|
if (isEmpty(files)) {
|
||||||
|
showFeedbackMessage(ALERT_CHOOSE_FILE, MessageTypesEnum.DANGER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var currentFile = files[i];
|
||||||
|
if (!isEmpty(fileExtensions) && fileExtensions.indexOf(getFileExt(currentFile)) === -1) {
|
||||||
|
showFeedbackMessage(ALERT_UNKNOWN_FILE_EXT, MessageTypesEnum.DANGER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentFile.size === 0) {
|
||||||
|
showFeedbackMessage(ALERT_EMPTY_FILE, MessageTypesEnum.DANGER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MAX_FILE_SIZE_MB != -1 && currentFile.size / SIZE_TO_MB > MAX_FILE_SIZE_MB) {
|
||||||
|
showFeedbackMessage(ALERT_MAX_FILE + " " + MAX_FILE_SIZE_MB + "Mb", MessageTypesEnum.DANGER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upload(currentFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buttonGroup.append(uploadButton);
|
||||||
|
|
||||||
|
var progressDiv = $("<div>")
|
||||||
|
.addClass("progress margined-top-10")
|
||||||
|
.css("background-color", "#FFFFFF");
|
||||||
|
progressDiv.insertAfter(div);
|
||||||
|
var progressBar = $("<div>")
|
||||||
|
.addClass("progress-bar")
|
||||||
|
.attr("role", "progressbar")
|
||||||
|
.css("width", "0%");
|
||||||
|
progressDiv.append(progressBar);
|
||||||
|
|
||||||
|
function getFileExt(file) {
|
||||||
|
return file.name.slice((Math.max(0, file.name.lastIndexOf(".")) || Infinity) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function upload(file) {
|
||||||
|
progressBar.css("width", "0%");
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.upload.onprogress = function (event) {
|
||||||
|
var curProgress = Math.floor(event.loaded / event.total * 100) + "%";
|
||||||
|
progressBar
|
||||||
|
.css("width", curProgress)
|
||||||
|
.text(curProgress);
|
||||||
|
};
|
||||||
|
xhr.onload = xhr.onerror = function () {
|
||||||
|
if (this.status === 200) {
|
||||||
|
errorHandler(JSON.parse(this.responseText), callback);
|
||||||
|
} else {
|
||||||
|
showFeedbackMessage(ERROR, MessageTypesEnum.DANGER);
|
||||||
|
console.error("error " + this.status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.open("POST", url, true);
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
xhr.send(formData);
|
||||||
|
}
|
||||||
|
}
|
@ -24,9 +24,11 @@
|
|||||||
<td th:text="${a.surname}"></td>
|
<td th:text="${a.surname}"></td>
|
||||||
<td th:text="${a.name}"></td>
|
<td th:text="${a.name}"></td>
|
||||||
<td th:text="${a.patronymic}"></td>
|
<td th:text="${a.patronymic}"></td>
|
||||||
<td th:text="${a.course.name}"></td>
|
<td th:if="${a.course != null}" th:text="${a.course.name}"></td>
|
||||||
|
<td th:if="${a.course == null}"></td>
|
||||||
<td th:text="${a.theme}"></td>
|
<td th:text="${a.theme}"></td>
|
||||||
<td th:text="${a.manager.name}"></td>
|
<td th:if="${a.manager != null}" th:text="${a.manager.name}"></td>
|
||||||
|
<td th:if="${a.manager == null}"></td>
|
||||||
<td>
|
<td>
|
||||||
<!-- Ссылка на редактирование -->
|
<!-- Ссылка на редактирование -->
|
||||||
<a th:href="@{'/admin/editAspirant/' + ${a.id}}" class="btn btn-sm btn-primary">
|
<a th:href="@{'/admin/editAspirant/' + ${a.id}}" class="btn btn-sm btn-primary">
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<title th:text="#{messages.app-name}">app-name</title>
|
<title th:text="#{messages.app-name}">app-name</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script>
|
<script type="text/javascript" src="/webjars/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/core.js"></script>
|
||||||
<script type="text/javascript" src="/webjars/bootstrap/4.3.0/js/bootstrap.bundle.min.js"></script>
|
<script type="text/javascript" src="/webjars/bootstrap/4.3.0/js/bootstrap.bundle.min.js"></script>
|
||||||
<link rel="stylesheet" href="/webjars/bootstrap/4.3.0/css/bootstrap.min.css"/>
|
<link rel="stylesheet" href="/webjars/bootstrap/4.3.0/css/bootstrap.min.css"/>
|
||||||
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
||||||
|
@ -7,17 +7,17 @@
|
|||||||
th:object="${report}"
|
th:object="${report}"
|
||||||
method="post">
|
method="post">
|
||||||
<input type="hidden" th:field="*{id}">
|
<input type="hidden" th:field="*{id}">
|
||||||
|
<input type="hidden" th:field="*{reportPeriodId}">
|
||||||
|
|
||||||
<div class="form-group" th:each="i, ind : ${indicators}">
|
<div class="form-group" th:each="rv, ind : *{reportValues}">
|
||||||
<p th:text="${i.name}"></p>
|
<hr/>
|
||||||
<p th:text="${i.proofDocuments}"></p>
|
<h5 th:text="${rv.indicator.name}"></h5>
|
||||||
<p th:text="'Максимальное количество баллов за показатель: '+ ${i.max}"></p>
|
<p th:text="${rv.indicator.proofDocuments}"></p>
|
||||||
|
<p th:text="'Максимальное количество баллов за показатель: '+ ${rv.indicator.max}"></p>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="form-group files-list" id="files-list">
|
||||||
<label for="formFile" class="form-label">Загрузка подтверждающих документов</label>
|
<label class="form-label">Загрузка подтверждающих документов</label>
|
||||||
<input class="form-control" type="file" id="formFile">
|
<div th:replace="/report/reportFilesListFragment"></div>
|
||||||
<input class="form-control" type="file" id="formFile2">
|
|
||||||
<input class="form-control" type="file" id="formFile3">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -30,16 +30,129 @@
|
|||||||
</button>
|
</button>
|
||||||
<a href="/report/reportList" class="btn btn-outline-dark">Отмена</a>
|
<a href="/report/reportList" class="btn btn-outline-dark">Отмена</a>
|
||||||
</form>
|
</form>
|
||||||
<div th:if="${indicators.totalPages > 0}" class="pagination">
|
<script type="text/javascript" src="/js/file-loader.js"></script>
|
||||||
<span style="float: left; padding: 5px 5px;">Показатели:</span>
|
<script>
|
||||||
</div>
|
/*<![CDATA[*/
|
||||||
<div th:if="${indicators.totalPages > 0}" class="pagination"
|
$(document).ready(function () {
|
||||||
th:each="pageNumber : ${pageNumbers}">
|
new FileLoader({
|
||||||
<a th:href="@{'/report/editReport/' + ${report.id}+'?size=' + ${indicators.size} + '&page='+${pageNumber}}"
|
div: "loader",
|
||||||
th:text=${pageNumber}
|
url: urlFileUpload,
|
||||||
th:class="${pageNumber == indicators.number+1} ? active"></a>
|
maxSize: -1,
|
||||||
</div>
|
extensions: [],
|
||||||
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
callback: function (response) {
|
||||||
<link rel="stylesheet" href="/webjars/bootstrap-glyphicons/bdd2cbfba0/css/bootstrap-glyphicons.css"/>
|
showFeedbackMessage("Файл успешно загружен");
|
||||||
|
console.debug(response);
|
||||||
|
|
||||||
|
addNewFile(response, $("#files-list"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('.selectpicker').selectpicker();
|
||||||
|
});
|
||||||
|
|
||||||
|
function sendPing() {
|
||||||
|
id = document.getElementById("projectId").value
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/projects/ping?projectId=" + id,
|
||||||
|
contentType: "application/json; charset=utf-8",
|
||||||
|
method: "POST",
|
||||||
|
success: function () {
|
||||||
|
showFeedbackMessage("Ping был отправлен", MessageTypesEnum.SUCCESS)
|
||||||
|
},
|
||||||
|
error: function (errorData) {
|
||||||
|
showFeedbackMessage(errorData.responseJSON.error.message, MessageTypesEnum.WARNING)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*]]>*/
|
||||||
|
function addNewFile(fileDto, listElement) {
|
||||||
|
var fileNumber = $('.files-list div.row').length;
|
||||||
|
|
||||||
|
var newFileRow = $("<div/>")
|
||||||
|
.attr("id", 'files' + fileNumber)
|
||||||
|
.addClass("row");
|
||||||
|
|
||||||
|
var idInput = $("<input/>")
|
||||||
|
.attr("type", "hidden")
|
||||||
|
.attr("id", "files" + fileNumber + ".id")
|
||||||
|
.attr("value", '')
|
||||||
|
.attr("name", "files[" + fileNumber + "].id");
|
||||||
|
newFileRow.append(idInput);
|
||||||
|
|
||||||
|
var flagInput = $("<input/>")
|
||||||
|
.attr("type", "hidden")
|
||||||
|
.attr("id", "files" + fileNumber + ".deleted")
|
||||||
|
.attr("value", "false")
|
||||||
|
.attr("name", "files[" + fileNumber + "].deleted");
|
||||||
|
newFileRow.append(flagInput);
|
||||||
|
|
||||||
|
var nameInput = $("<input/>")
|
||||||
|
.attr("type", "hidden")
|
||||||
|
.attr("id", "files" + fileNumber + ".name")
|
||||||
|
.attr("value", fileDto.fileName)
|
||||||
|
.attr("name", "files[" + fileNumber + "].name");
|
||||||
|
newFileRow.append(nameInput);
|
||||||
|
|
||||||
|
var tmpFileNameInput = $("<input/>")
|
||||||
|
.attr("type", "hidden")
|
||||||
|
.attr("id", "files" + fileNumber + ".tmpFileName")
|
||||||
|
.attr("value", fileDto.tmpFileName)
|
||||||
|
.attr("name", "files[" + fileNumber + "].tmpFileName");
|
||||||
|
newFileRow.append(tmpFileNameInput);
|
||||||
|
|
||||||
|
var nextDiv = $("<div/>")
|
||||||
|
.addClass("col-2");
|
||||||
|
|
||||||
|
var nextA = $("<a/>")
|
||||||
|
.addClass("btn btn-danger float-right")
|
||||||
|
.attr("onclick", "$('#files" + fileNumber + "\\\\.deleted').val('true'); $('#files" + fileNumber + "').hide();")
|
||||||
|
.append(($("<span/>").attr("aria-hidden", "true")).append($("<i/>").addClass("fa fa-times")))
|
||||||
|
;
|
||||||
|
nextDiv.append(nextA)
|
||||||
|
newFileRow.append(nextDiv);
|
||||||
|
|
||||||
|
var nameDiv = $("<div/>")
|
||||||
|
.addClass("col-10")
|
||||||
|
.append($("<a/>").text(fileDto.fileName)
|
||||||
|
.attr("href", 'javascript:void(0)')
|
||||||
|
.attr("onclick", "downloadFile('" + fileDto.tmpFileName + "',null,'" + fileDto.fileName + "')"));
|
||||||
|
newFileRow.append(nameDiv);
|
||||||
|
|
||||||
|
listElement.append(newFileRow);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadFile(tmpName, fileId, downloadName) {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
if (fileId != null) xhr.open('GET', urlFileDownload + fileId);
|
||||||
|
if (tmpName != null) xhr.open('GET', urlFileDownloadTmp + tmpName);
|
||||||
|
xhr.responseType = 'blob';
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
if (fileId != null) formData.append("file-id", fileId);
|
||||||
|
if (tmpName != null) formData.append("tmp-file-name", tmpName);
|
||||||
|
|
||||||
|
xhr.send(formData);
|
||||||
|
|
||||||
|
xhr.onload = function () {
|
||||||
|
if (this.status == 200) {
|
||||||
|
console.debug(this.response);
|
||||||
|
var blob = new Blob([this.response], {type: '*'});
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.style = "display: none";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
let url = window.URL.createObjectURL(blob);
|
||||||
|
a.href = url;
|
||||||
|
a.download = downloadName;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div th:fragment="filesList">
|
||||||
|
<th:block th:each="file, rowStat : ${rv.files}">
|
||||||
|
|
||||||
|
<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}"/>
|
||||||
|
<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>
|
||||||
|
</th:block>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="loader">Загрузить файл:</label>
|
||||||
|
<div id="loader">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -29,7 +29,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<a href="/report/editReport/0" class="btn btn-outline-dark" th:if="${canCreate}">
|
<a th:href="@{'/report/editReport/0/'+ ${reportListForm.reportPeriod.id}}" class="btn btn-outline-dark"
|
||||||
|
th:if="${canCreate}">
|
||||||
<i class="fa fa-plus-square" aria-hidden="true"> Добавить отчет</i>
|
<i class="fa fa-plus-square" aria-hidden="true"> Добавить отчет</i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user