diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/config/InitConfig.java b/src/main/java/org/sadtech/bot/bitbucketbot/config/InitProperty.java similarity index 92% rename from src/main/java/org/sadtech/bot/bitbucketbot/config/InitConfig.java rename to src/main/java/org/sadtech/bot/bitbucketbot/config/InitProperty.java index e2e94c5..32f320c 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/config/InitConfig.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/config/InitProperty.java @@ -9,7 +9,7 @@ import org.springframework.stereotype.Component; @Setter @Component @ConfigurationProperties(prefix = "bitbucketbot.init") -public class InitConfig { +public class InitProperty { private Long startCommentId; diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/domain/Pagination.java b/src/main/java/org/sadtech/bot/bitbucketbot/domain/Pagination.java deleted file mode 100644 index 2fc8a47..0000000 --- a/src/main/java/org/sadtech/bot/bitbucketbot/domain/Pagination.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.sadtech.bot.bitbucketbot.domain; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class Pagination { - - private Integer page; - private Integer size; - - public static Pagination of(Integer page, Integer size) { - return new Pagination(page, size); - } - - -} diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/domain/change/task/TaskChange.java b/src/main/java/org/sadtech/bot/bitbucketbot/domain/change/task/TaskChange.java index 79556dc..8e0092b 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/domain/change/task/TaskChange.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/domain/change/task/TaskChange.java @@ -1,6 +1,5 @@ package org.sadtech.bot.bitbucketbot.domain.change.task; -import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; import org.sadtech.bot.bitbucketbot.domain.change.Change; @@ -10,13 +9,12 @@ import java.util.Set; @Getter @EqualsAndHashCode(callSuper = true) -public class TaskChange extends Change { +public abstract class TaskChange extends Change { protected final String authorName; protected final String url; protected final String messageTask; - @Builder protected TaskChange( ChangeType type, Set telegramIds, @@ -29,4 +27,5 @@ public class TaskChange extends Change { this.url = url; this.messageTask = messageTask; } + } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Comment.java b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Comment.java index 51adb18..babb059 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Comment.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Comment.java @@ -9,8 +9,6 @@ import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.Table; @@ -20,7 +18,7 @@ import java.util.Set; @Getter @Setter @Entity -@Table(name = "pull_request_comment") +@Table(name = "comment") @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class Comment { @@ -29,7 +27,6 @@ public class Comment { */ @Id @Column(name = "id") - @GeneratedValue(strategy = GenerationType.IDENTITY) @EqualsAndHashCode.Include private Long id; diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Reviewer.java b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Reviewer.java index ed277f1..01d2ac3 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Reviewer.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Reviewer.java @@ -26,7 +26,7 @@ import javax.persistence.Table; @Entity @Getter @Setter -@Table(name = "pull_request_reviewer") +@Table(name = "reviewer") @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class Reviewer { @@ -42,8 +42,8 @@ public class Reviewer { /** * Пользователь */ - @Column(name = "user_login") - private String userLogin; + @Column(name = "person_login") + private String personLogin; /** * Статус diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Task.java b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Task.java index 10ca559..07083d8 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Task.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/domain/entity/Task.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.Setter; import org.sadtech.bot.bitbucketbot.domain.TaskStatus; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -12,13 +13,16 @@ import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.OneToMany; import javax.persistence.Table; import java.time.LocalDateTime; +import java.util.List; @Entity @Getter @Setter -@Table(name = "pull_request_task") +@Table(name = "task") @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class Task { @@ -62,4 +66,8 @@ public class Task { @Column(name = "author_login") private String author; + @JoinTable + @OneToMany(cascade = CascadeType.ALL) + private List comments; + } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/repository/CommentRepository.java b/src/main/java/org/sadtech/bot/bitbucketbot/repository/CommentRepository.java new file mode 100644 index 0000000..4c1d720 --- /dev/null +++ b/src/main/java/org/sadtech/bot/bitbucketbot/repository/CommentRepository.java @@ -0,0 +1,25 @@ +package org.sadtech.bot.bitbucketbot.repository; + +import lombok.NonNull; +import org.sadtech.basic.context.repository.SimpleManagerRepository; +import org.sadtech.bot.bitbucketbot.domain.entity.Comment; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * // TODO: 08.09.2020 Добавить описание. + * + * @author upagge 08.09.2020 + */ +public interface CommentRepository extends SimpleManagerRepository { + + Optional findFirstByOrderByIdDesc(); + + List findByCreateDateBetween(@NonNull LocalDateTime dateFrom, @NonNull LocalDateTime dateTo); + + List findAllById(@NonNull Set ids); + +} diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/ChangeRepositoryImpl.java b/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/ChangeRepositoryImpl.java index 6b7b64e..c0fe697 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/ChangeRepositoryImpl.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/ChangeRepositoryImpl.java @@ -11,7 +11,7 @@ import java.util.List; @Repository public class ChangeRepositoryImpl implements ChangeRepository { - private List list = new ArrayList<>(); + private final List list = new ArrayList<>(); private long count = 0; @Override @@ -23,8 +23,7 @@ public class ChangeRepositoryImpl implements ChangeRepository { @Override public List getAll() { - final ArrayList changes = new ArrayList<>(list); - return changes; + return new ArrayList<>(list); } @Override diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/CommentRepositoryImpl.java b/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/CommentRepositoryImpl.java new file mode 100644 index 0000000..db4d1a2 --- /dev/null +++ b/src/main/java/org/sadtech/bot/bitbucketbot/repository/impl/CommentRepositoryImpl.java @@ -0,0 +1,45 @@ +package org.sadtech.bot.bitbucketbot.repository.impl; + +import lombok.NonNull; +import org.sadtech.basic.database.repository.manager.AbstractSimpleManagerRepository; +import org.sadtech.bot.bitbucketbot.domain.entity.Comment; +import org.sadtech.bot.bitbucketbot.repository.CommentRepository; +import org.sadtech.bot.bitbucketbot.repository.jpa.CommentRepositoryJpa; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * // TODO: 08.09.2020 Добавить описание. + * + * @author upagge 08.09.2020 + */ +@Repository +public class CommentRepositoryImpl extends AbstractSimpleManagerRepository implements CommentRepository { + + private final CommentRepositoryJpa repositoryJpa; + + public CommentRepositoryImpl(CommentRepositoryJpa repositoryJpa) { + super(repositoryJpa); + this.repositoryJpa = repositoryJpa; + } + + @Override + public Optional findFirstByOrderByIdDesc() { + return repositoryJpa.findFirstByOrderByIdDesc(); + } + + @Override + public List findByCreateDateBetween(LocalDateTime dateFrom, LocalDateTime dateTo) { + return repositoryJpa.findByCreateDateBetween(dateFrom, dateTo); + } + + @Override + public List findAllById(@NonNull Set ids) { + return repositoryJpa.findAllById(ids); + } + +} diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepository.java b/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepositoryJpa.java similarity index 60% rename from src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepository.java rename to src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepositoryJpa.java index 00dd203..30f9e88 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepository.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/CommentRepositoryJpa.java @@ -1,5 +1,6 @@ package org.sadtech.bot.bitbucketbot.repository.jpa; +import lombok.NonNull; import org.sadtech.bot.bitbucketbot.domain.entity.Comment; import org.springframework.data.jpa.repository.JpaRepository; @@ -7,10 +8,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Optional; -public interface CommentRepository extends JpaRepository { +public interface CommentRepositoryJpa extends JpaRepository { Optional findFirstByOrderByIdDesc(); - List findByCreateDateBetween(LocalDateTime dateFrom, LocalDateTime dateTo); + List findByCreateDateBetween(@NonNull LocalDateTime dateFrom, @NonNull LocalDateTime dateTo); } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/PullRequestsRepositoryJpa.java b/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/PullRequestsRepositoryJpa.java index a9c8d63..2282041 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/PullRequestsRepositoryJpa.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/repository/jpa/PullRequestsRepositoryJpa.java @@ -27,7 +27,7 @@ public interface PullRequestsRepositoryJpa extends JpaRepositoryImplementation

id); - @Query("SELECT p FROM PullRequest p LEFT JOIN p.reviewers r WHERE r.userLogin=:reviewer AND r.status =:reviewerStatus AND p.status IN :pullRequestStatus") + @Query("SELECT p FROM PullRequest p LEFT JOIN p.reviewers r WHERE r.personLogin=:reviewer AND r.status =:reviewerStatus AND p.status IN :pullRequestStatus") List findAllByReviewerAndStatuses(@Param("reviewer") String reviewer, @Param("reviewerStatus") ReviewerStatus reviewerStatus, @Param("pullRequestStatus") Set pullRequestStatus); @Query("SELECT p FROM PullRequest p LEFT JOIN p.reviewers r WHERE p.authorLogin=:author AND r.status=:reviewerStatus") diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/scheduler/parser/CommentAndTaskScheduler.java b/src/main/java/org/sadtech/bot/bitbucketbot/scheduler/parser/CommentAndTaskScheduler.java index e6827cf..4fad475 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/scheduler/parser/CommentAndTaskScheduler.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/scheduler/parser/CommentAndTaskScheduler.java @@ -1,22 +1,24 @@ package org.sadtech.bot.bitbucketbot.scheduler.parser; import lombok.RequiredArgsConstructor; +import org.sadtech.bot.bitbucketbot.service.parser.CommentAndTaskParser; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class CommentAndTaskScheduler { -// private final CommentAndTaskParser commentAndTaskParser; -// -// @Scheduled(cron = "") -// public void scanNewCommentAndTask() { -// commentAndTaskParser.scanNewCommentAndTask(); -// } -// -// @Scheduled(cron = "") -// public void scanOldComment() { -// commentAndTaskParser.scanOldComment(); -// } + private final CommentAndTaskParser commentAndTaskParser; + + @Scheduled(cron = "0 */1 * * * *") + public void scanNewCommentAndTask() { + commentAndTaskParser.scanNewCommentAndTask(); + } + + // @Scheduled(cron = "0 */1 * * * *") + public void scanOldComment() { + commentAndTaskParser.scanOldComment(); + } } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/ChangeService.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/ChangeService.java index 70d6994..dc04a0d 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/ChangeService.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/ChangeService.java @@ -1,11 +1,6 @@ package org.sadtech.bot.bitbucketbot.service; -import lombok.NonNull; import org.sadtech.bot.bitbucketbot.domain.change.Change; -import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange; -import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange; -import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest; -import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange; import java.util.List; @@ -17,11 +12,7 @@ import java.util.List; */ public interface ChangeService { - NewPrChange create(@NonNull PullRequest newPullRequest); - - UpdatePrChange createUpdatePr(@NonNull PullRequest oldPullRequest, @NonNull PullRequest newPullRequest); - - Change createReviewersPr(String prName, String prUrl, String authorLogin, List reviewerChanges); + void save(T task); /** * Позволяет получить новые изменения. diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/CommentService.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/CommentService.java index c700dd6..37a434f 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/CommentService.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/CommentService.java @@ -1,33 +1,19 @@ package org.sadtech.bot.bitbucketbot.service; import lombok.NonNull; -import org.sadtech.bot.bitbucketbot.domain.Pagination; +import org.sadtech.basic.context.service.SimpleManagerService; import org.sadtech.bot.bitbucketbot.domain.entity.Comment; -import org.springframework.data.domain.Page; import java.time.LocalDateTime; import java.util.List; -import java.util.Optional; import java.util.Set; -public interface CommentService { +public interface CommentService extends SimpleManagerService { Long getLastCommentId(); - Page getAll(@NonNull Pagination pagination); - - @NonNull - List getAllBetweenDate(LocalDateTime dateFrom, LocalDateTime dateTo); - - Comment create(@NonNull Comment comment); - - void delete(@NonNull Long id); - - Optional getProxyById(@NonNull Long id); - - List createAll(List newComments); - - Comment update(Comment comment); + List getAllBetweenDate(@NonNull LocalDateTime dateFrom, @NonNull LocalDateTime dateTo); List getAllById(@NonNull Set ids); + } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/PullRequestJsonConverter.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/PullRequestJsonConverter.java index 239d1c5..db9ff2c 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/PullRequestJsonConverter.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/PullRequestJsonConverter.java @@ -73,7 +73,7 @@ public class PullRequestJsonConverter implements Converter { final Reviewer reviewer = new Reviewer(); - reviewer.setUserLogin(jsonReviewer.getUser().getName()); + reviewer.setPersonLogin(jsonReviewer.getUser().getName()); reviewer.setStatus(convertStatusReviewer(jsonReviewer.getStatus())); return reviewer; } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/ResultScanToComment.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/ResultScanToComment.java index 8c3e828..958e875 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/ResultScanToComment.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/converter/ResultScanToComment.java @@ -16,6 +16,7 @@ public class ResultScanToComment implements Converter { public Comment convert(ResultScan resultScan) { final CommentJson commentJson = resultScan.getCommentJson(); final Comment comment = new Comment(); + comment.setId(commentJson.getId()); comment.setCreateDate(commentJson.getCreatedDate()); comment.setAuthor(commentJson.getAuthor().getName()); comment.setPullRequestId(resultScan.getPullRequestId()); diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/ChangeServiceImpl.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/ChangeServiceImpl.java index d240288..a8b4f6d 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/ChangeServiceImpl.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/ChangeServiceImpl.java @@ -1,76 +1,22 @@ package org.sadtech.bot.bitbucketbot.service.impl; -import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.sadtech.bot.bitbucketbot.domain.change.Change; -import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange; -import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ReviewersPrChange; -import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange; -import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest; -import org.sadtech.bot.bitbucketbot.domain.entity.Reviewer; -import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange; import org.sadtech.bot.bitbucketbot.repository.ChangeRepository; import org.sadtech.bot.bitbucketbot.service.ChangeService; -import org.sadtech.bot.bitbucketbot.service.PersonService; import org.springframework.stereotype.Service; -import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class ChangeServiceImpl implements ChangeService { private final ChangeRepository changeRepository; - private final PersonService personService; @Override - public NewPrChange create(@NonNull PullRequest newPullRequest) { - return changeRepository.add( - NewPrChange.builder() - .author(newPullRequest.getAuthorLogin()) - .description(newPullRequest.getDescription()) - .title(newPullRequest.getTitle()) - .url(newPullRequest.getUrl()) - .telegramIds(getReviewerTelegrams(newPullRequest.getReviewers())) - .build() - ); - } - - @Override - public UpdatePrChange createUpdatePr(@NonNull PullRequest oldPullRequest, @NonNull PullRequest newPullRequest) { - return changeRepository.add( - UpdatePrChange.builder() - .author(oldPullRequest.getAuthorLogin()) - .name(newPullRequest.getTitle()) - .telegramIds(getReviewerTelegrams(newPullRequest.getReviewers())) - .url(newPullRequest.getUrl()) - .build() - ); - } - - @Override - public Change createReviewersPr(String prName, String prUrl, String authorLogin, List reviewerChanges) { - return changeRepository.add( - ReviewersPrChange.builder() - .title(prName) - .url(prUrl) - .telegramIds( - personService.getAllTelegramIdByLogin(Collections.singleton(authorLogin)) - ) - .reviewerChanges(reviewerChanges) - .build() - ); - } - - private Set getReviewerTelegrams(@NonNull List reviewers) { - return personService.getAllTelegramIdByLogin( - reviewers.stream() - .map(Reviewer::getUserLogin) - .collect(Collectors.toSet()) - ); + public void save(T change) { + changeRepository.add(change); } @Override @@ -80,5 +26,4 @@ public class ChangeServiceImpl implements ChangeService { return changes; } - } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/CommentServiceImpl.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/CommentServiceImpl.java index a74bdeb..90e0935 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/CommentServiceImpl.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/CommentServiceImpl.java @@ -1,79 +1,72 @@ package org.sadtech.bot.bitbucketbot.service.impl; -//@Service -//@RequiredArgsConstructor -//public class CommentServiceImpl implements CommentService { -// -// private final PersonService personService; -// private final CommentRepository commentRepository; -// private final PullRequestsService pullRequestsService; -// private final InitConfig initConfig; -// -// @Override -// public Long getLastCommentId() { -// return commentRepository.findFirstByOrderByIdDesc().map(Comment::getId).orElse(getInitCommentId()); -// } -// -// private Long getInitCommentId() { -// return initConfig.getStartCommentId() != null ? initConfig.getStartCommentId() : 0L; -// } -// -// @Override -// public Page getAll(@NonNull Pagination pagination) { -// return commentRepository.findAll(PageRequest.of(pagination.getPage(), pagination.getSize())); -// } -// -// @Override -// public @NonNull List getAllBetweenDate(LocalDateTime dateFrom, LocalDateTime dateTo) { -// return commentRepository.findByDateBetween(dateFrom, dateTo); -// } -// -// @Override -// public Comment create(@NonNull Comment comment) { -// comment.setAuthor(personService.getProxyByLogin( -// comment.getAuthor().getLogin()).orElseThrow(() -> new NotFoundException("")) -// ); -// comment.setPullRequest( -// pullRequestsService.getProxyById(comment.getPullRequest().getId()).orElseThrow(() -> new NotFoundException("")) -// ); -// return commentRepository.save(comment); -// } -// -// @Override -// public void delete(@NonNull Long id) { -// commentRepository.deleteById(id); -// } -// -// @Override -// public Optional getProxyById(@NonNull Long id) { -// return Optional.ofNullable(commentRepository.getOne(id)); -// } -// -// @Override -// @Transactional -// public List createAll(List newComments) { -// return newComments.stream() -// .map(this::create) -// .collect(Collectors.toList()); -// } -// -// @Override -// public Comment update(Comment comment) { -// final Comment oldComment = commentRepository.findById(comment.getId()) -// .orElseThrow(() -> new NotFoundException("Комментарий не найден")); -// -// if (oldComment.getBitbucketVersion().equals(comment.getBitbucketVersion())) { -// oldComment.setBitbucketVersion(comment.getBitbucketVersion()); -// oldComment.setMessage(oldComment.getMessage()); -// return commentRepository.save(oldComment); -// } -// -// return oldComment; -// } -// -// @Override -// public List getAllById(@NonNull Set ids) { -// return commentRepository.findAllById(ids); -// } -// -//} +import lombok.NonNull; +import org.sadtech.basic.core.service.AbstractSimpleManagerService; +import org.sadtech.bot.bitbucketbot.config.InitProperty; +import org.sadtech.bot.bitbucketbot.domain.entity.Comment; +import org.sadtech.bot.bitbucketbot.exception.NotFoundException; +import org.sadtech.bot.bitbucketbot.repository.CommentRepository; +import org.sadtech.bot.bitbucketbot.service.CommentService; +import org.sadtech.bot.bitbucketbot.service.PersonService; +import org.sadtech.bot.bitbucketbot.service.PullRequestsService; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +@Service +public class CommentServiceImpl extends AbstractSimpleManagerService implements CommentService { + + private final CommentRepository commentRepository; + private final PersonService personService; + private final PullRequestsService pullRequestsService; + private final InitProperty initProperty; + + public CommentServiceImpl(CommentRepository commentRepository, PersonService personService, PullRequestsService pullRequestsService, InitProperty initProperty) { + super(commentRepository); + this.personService = personService; + this.commentRepository = commentRepository; + this.pullRequestsService = pullRequestsService; + this.initProperty = initProperty; + } + + @Override + public Long getLastCommentId() { + return commentRepository.findFirstByOrderByIdDesc().map(Comment::getId).orElse(getInitCommentId()); + } + + private Long getInitCommentId() { + return initProperty.getStartCommentId() != null ? initProperty.getStartCommentId() : 0L; + } + + @Override + public List getAllBetweenDate(@NonNull LocalDateTime dateFrom, LocalDateTime dateTo) { + return commentRepository.findByCreateDateBetween(dateFrom, dateTo); + } + + @Override + public Comment create(@NonNull Comment comment) { + return commentRepository.save(comment); + } + + @Override + public Comment update(Comment comment) { + final Comment oldComment = commentRepository.findById(comment.getId()) + .orElseThrow(() -> new NotFoundException("Комментарий не найден")); + + if (oldComment.getBitbucketVersion().equals(comment.getBitbucketVersion())) { + oldComment.setBitbucketVersion(comment.getBitbucketVersion()); + oldComment.setMessage(oldComment.getMessage()); + return commentRepository.save(oldComment); + } + + return oldComment; + } + + @Override + public List getAllById(@NonNull Set ids) { + return commentRepository.findAllById(ids); + } + +} diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/PullRequestsServiceImpl.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/PullRequestsServiceImpl.java index f2e96c0..47ebb37 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/PullRequestsServiceImpl.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/impl/PullRequestsServiceImpl.java @@ -11,6 +11,10 @@ import org.sadtech.basic.filter.criteria.CriteriaQuery; import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr; import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus; import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus; +import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ConflictPrChange; +import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange; +import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ReviewersPrChange; +import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange; import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest; import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest_; import org.sadtech.bot.bitbucketbot.domain.entity.Reviewer; @@ -19,11 +23,13 @@ import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange; import org.sadtech.bot.bitbucketbot.exception.UpdateException; import org.sadtech.bot.bitbucketbot.repository.PullRequestsRepository; import org.sadtech.bot.bitbucketbot.service.ChangeService; +import org.sadtech.bot.bitbucketbot.service.PersonService; import org.sadtech.bot.bitbucketbot.service.PullRequestsService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -35,16 +41,18 @@ public class PullRequestsServiceImpl extends AbstractSimpleManagerService filterService; protected PullRequestsServiceImpl( PullRequestsRepository pullRequestsRepository, ChangeService changeService, - @Qualifier("pullRequestFilterService") FilterService pullRequestsFilterService + PersonService personService, @Qualifier("pullRequestFilterService") FilterService pullRequestsFilterService ) { super(pullRequestsRepository); this.changeService = changeService; this.pullRequestsRepository = pullRequestsRepository; + this.personService = personService; this.filterService = pullRequestsFilterService; } @@ -54,14 +62,43 @@ public class PullRequestsServiceImpl extends AbstractSimpleManagerService getReviewerTelegrams(@NonNull List reviewers) { + return personService.getAllTelegramIdByLogin( + reviewers.stream() + .map(Reviewer::getPersonLogin) + .collect(Collectors.toSet()) + ); + } + @Override public PullRequest update(@NonNull PullRequest pullRequest) { final PullRequest oldPullRequest = findAndFillId(pullRequest); + + if (!oldPullRequest.isConflict() && pullRequest.isConflict()) { + changeService.save( + ConflictPrChange.builder() + .name(pullRequest.getTitle()) + .url(pullRequest.getUrl()) + .telegramIds( + personService.getAllTelegramIdByLogin(Collections.singleton(pullRequest.getAuthorLogin())) + ) + .build() + ); + } + oldPullRequest.setBitbucketVersion(pullRequest.getBitbucketVersion()); oldPullRequest.setConflict(pullRequest.isConflict()); oldPullRequest.setTitle(pullRequest.getTitle()); @@ -71,7 +108,14 @@ public class PullRequestsServiceImpl extends AbstractSimpleManagerService oldReviewers = oldPullRequest.getReviewers().stream() - .collect(Collectors.toMap(Reviewer::getUserLogin, reviewer -> reviewer)); + .collect(Collectors.toMap(Reviewer::getPersonLogin, reviewer -> reviewer)); final Map newReviewers = newPullRequest.getReviewers().stream() - .collect(Collectors.toMap(Reviewer::getUserLogin, reviewer -> reviewer)); + .collect(Collectors.toMap(Reviewer::getPersonLogin, reviewer -> reviewer)); final List reviewerChanges = new ArrayList<>(); for (Reviewer newReviewer : newReviewers.values()) { - if (oldReviewers.containsKey(newReviewer.getUserLogin())) { - final Reviewer oldReviewer = oldReviewers.get(newReviewer.getUserLogin()); + if (oldReviewers.containsKey(newReviewer.getPersonLogin())) { + final Reviewer oldReviewer = oldReviewers.get(newReviewer.getPersonLogin()); final ReviewerStatus oldStatus = oldReviewer.getStatus(); final ReviewerStatus newStatus = newReviewer.getStatus(); if (!oldStatus.equals(newStatus)) { - reviewerChanges.add(ReviewerChange.ofOld(oldReviewer.getUserLogin(), oldStatus, newStatus)); + reviewerChanges.add(ReviewerChange.ofOld(oldReviewer.getPersonLogin(), oldStatus, newStatus)); oldReviewer.setStatus(newStatus); } } else { - reviewerChanges.add(ReviewerChange.ofNew(newReviewer.getUserLogin(), newReviewer.getStatus())); + reviewerChanges.add(ReviewerChange.ofNew(newReviewer.getPersonLogin(), newReviewer.getStatus())); newReviewer.setPullRequest(oldPullRequest); oldPullRequest.getReviewers().add(newReviewer); } @@ -103,13 +147,22 @@ public class PullRequestsServiceImpl extends AbstractSimpleManagerService oldIds.contains(e.getKey())) - .map(e -> ReviewerChange.ofDeleted(e.getValue().getUserLogin())) + .map(e -> ReviewerChange.ofDeleted(e.getValue().getPersonLogin())) .collect(Collectors.toList()) ); oldPullRequest.getReviewers() - .removeIf(reviewer -> oldIds.contains(reviewer.getUserLogin())); + .removeIf(reviewer -> oldIds.contains(reviewer.getPersonLogin())); if (!reviewerChanges.isEmpty()) { - changeService.createReviewersPr(newPullRequest.getTitle(), newPullRequest.getUrl(), newPullRequest.getAuthorLogin(), reviewerChanges); + changeService.save( + ReviewersPrChange.builder() + .title(newPullRequest.getTitle()) + .url(newPullRequest.getUrl()) + .telegramIds( + personService.getAllTelegramIdByLogin(Collections.singleton(newPullRequest.getAuthorLogin())) + ) + .reviewerChanges(reviewerChanges) + .build() + ); } } diff --git a/src/main/java/org/sadtech/bot/bitbucketbot/service/parser/CommentAndTaskParser.java b/src/main/java/org/sadtech/bot/bitbucketbot/service/parser/CommentAndTaskParser.java index 2abed42..47ecfb8 100644 --- a/src/main/java/org/sadtech/bot/bitbucketbot/service/parser/CommentAndTaskParser.java +++ b/src/main/java/org/sadtech/bot/bitbucketbot/service/parser/CommentAndTaskParser.java @@ -1,8 +1,43 @@ package org.sadtech.bot.bitbucketbot.service.parser; +import lombok.NonNull; import lombok.RequiredArgsConstructor; +import org.sadtech.basic.context.page.Sheet; +import org.sadtech.basic.core.page.PaginationImpl; +import org.sadtech.bot.bitbucketbot.config.InitProperty; +import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty; +import org.sadtech.bot.bitbucketbot.config.properties.CommentSchedulerProperty; +import org.sadtech.bot.bitbucketbot.domain.Answer; +import org.sadtech.bot.bitbucketbot.domain.change.comment.AnswerCommentChange; +import org.sadtech.bot.bitbucketbot.domain.change.comment.CommentChange; +import org.sadtech.bot.bitbucketbot.domain.entity.Comment; +import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest; +import org.sadtech.bot.bitbucketbot.domain.entity.Task; +import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson; +import org.sadtech.bot.bitbucketbot.dto.bitbucket.Severity; +import org.sadtech.bot.bitbucketbot.service.ChangeService; +import org.sadtech.bot.bitbucketbot.service.CommentService; +import org.sadtech.bot.bitbucketbot.service.PersonService; +import org.sadtech.bot.bitbucketbot.service.PullRequestsService; +import org.sadtech.bot.bitbucketbot.service.TaskService; +import org.sadtech.bot.bitbucketbot.service.Utils; +import org.sadtech.bot.bitbucketbot.service.executor.DataScan; +import org.sadtech.bot.bitbucketbot.service.executor.ResultScan; +import org.sadtech.bot.bitbucketbot.service.impl.ExecutorScanner; +import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Component; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + /** *

Поиск новых комментариев и задач.

*

К несчастью, у битбакета не очень удобный API, и у них таска это то же самое что и комментарий, только с флагом

@@ -10,168 +45,161 @@ import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class CommentAndTaskParser { -// -// private static final Pattern PATTERN = Pattern.compile("@[\\w]+"); -// -// private final CommentService commentService; -// private final PullRequestsService pullRequestsService; -// private final PersonService personService; -// private final ChangeService changeService; -// private final ExecutorScanner executorScanner; -// private final TaskService taskService; -// private final ConversionService conversionService; -// -// private final BitbucketProperty bitbucketProperty; -// private final CommentSchedulerProperty commentSchedulerProperty; -// -// public void scanNewCommentAndTask() { -// long commentId = getLastIdCommentOrTask() + 1; -// int count = 0; -// do { -// final List dataScans = generatingLinksToPossibleComments(commentId); -// executorScanner.registration(dataScans); -// final List resultScans = executorScanner.getResult(); -// if (!resultScans.isEmpty()) { -// processingComments(resultScans); -// processingTasks(resultScans); -// count = 0; -// } -// } while (count++ < commentSchedulerProperty.getNoCommentCount()); -// } -// -// private long getLastIdCommentOrTask() { -// return Long.max(commentService.getLastCommentId(), taskService.getLastTaskId()); -// } -// -// private void processingComments(List resultScans) { -// final List newComments = commentService.createAll(getCommentsByResultScan(resultScans)); -// newComments.forEach(this::notificationPersonal); -// } -// -// private void processingTasks(List resultScans) { -// final List newTasks = taskService.createAll(getTaskByResultScan(resultScans)); -// newTasks.forEach(this::notificationNewTask); -// } -// -// private List generatingLinksToPossibleComments(@NonNull Long commentId) { -// List commentUrls = new ArrayList<>(); -// for (int i = 0; i < 5; i++) { -// int page = 0; -// Page pullRequestPage = pullRequestsService.getAll( -// Pagination.of(page, commentSchedulerProperty.getCommentCount()) -// ); -// while (pullRequestPage.hasContent()) { -// long finalCommentId = commentId; -// commentUrls.addAll(pullRequestPage.getContent().stream() -// .map( -// pullRequest -> new DataScan( -// getCommentUrl(finalCommentId, pullRequest), -// pullRequest.getId() -// ) -// ) -// .collect(Collectors.toList())); -// pullRequestPage = pullRequestsService.getAll( -// Pagination.of(++page, commentSchedulerProperty.getCommentCount()) -// ); -// } -// commentId++; -// } -// return commentUrls; -// } -// -// private List getCommentsByResultScan(List resultScans) { -// return resultScans.stream() -// .filter(resultScan -> Severity.NORMAL.equals(resultScan.getCommentJson().getSeverity())) -// .map(resultScan -> conversionService.convert(resultScan, Comment.class)) -// .collect(Collectors.toList()); -// } -// -// private List getTaskByResultScan(List resultScans) { -// return resultScans.stream() -// .filter(commentJson -> Severity.BLOCKER.equals(commentJson.getCommentJson().getSeverity())) -// .map(resultScan -> conversionService.convert(resultScan, Task.class)) -// .collect(Collectors.toList()); -// } -// -// private String getCommentUrl(long commentId, PullRequest pullRequest) { -// return bitbucketProperty.getUrlPullRequestComment() -// .replace("{projectKey}", pullRequest.getProjectKey()) -// .replace("{repositorySlug}", pullRequest.getRepositorySlug()) -// .replace("{pullRequestId}", pullRequest.getBitbucketId().toString()) -// .replace("{commentId}", String.valueOf(commentId)); -// } -// -// private void notificationPersonal(@NonNull Comment comment) { -// Matcher matcher = PATTERN.matcher(comment.getMessage()); -// Set recipientsLogins = new HashSet<>(); -// while (matcher.find()) { -// final String login = matcher.group(0).replace("@", ""); -// recipientsLogins.add(login); -// } -// final Set recipientsIds = personService.getAllTelegramIdByLogin(recipientsLogins); -// changeService.add( -// CommentChange.builder() -// .authorName(comment.getAuthor().getLogin()) -// .url(comment.getUrl()) -// .telegramIds(recipientsIds) -// .message(comment.getMessage()) -// .build() -// ); -// } -// -// private void notificationNewTask(@NonNull Task task) { -// changeService.add( -// TaskChange.builder() -// .authorName(task.getAuthor().getFullName()) -// .messageTask(task.getDescription()) -// .type(ChangeType.NEW_TASK) -// .url(task.getUrl()) -// .telegramIds(Collections.singleton(task.getPullRequest().getAuthor().getTelegramId())) -// .build() -// ); -// } -// -// public void scanOldComment() { -// @NonNull final List comments = commentService.getAllBetweenDate( -// LocalDateTime.now().minusDays(10), LocalDateTime.now() -// ); -// for (Comment oldComment : comments) { -// final Optional optCommentJson = Utils.urlToJson( -// oldComment.getUrl(), -// bitbucketProperty.getToken(), -// CommentJson.class -// ); -// final Comment newComment = commentService.update(conversionService.convert(oldComment, Comment.class)); -// -// if (optCommentJson.isPresent()) { -// final CommentJson commentJson = optCommentJson.get(); -// notifyNewCommentAnswers(oldComment, newComment); -// } -// } -// } -// -// private void notifyNewCommentAnswers(Comment oldComment, Comment newComment) { -// final Set oldAnswerIds = oldComment.getAnswers(); -// final Set newAnswerIds = newComment.getAnswers(); -// if (!newAnswerIds.isEmpty()) { -// final List newAnswers = commentService.getAllById(newAnswerIds).stream() -// .filter(comment -> !oldAnswerIds.contains(comment.getId())) -// .collect(Collectors.toList()); -// changeService.add( -// AnswerCommentChange.builder() -// .telegramIds( -// Collections.singleton(newComment.getAuthor().getTelegramId()) -// ) -// .url(newComment.getPullRequest().getUrl()) -// .youMessage(newComment.getMessage()) -// .answers( -// newAnswers.stream() -// .map(comment -> Answer.of(comment.getAuthor().getFullName(), comment.getMessage())) -// .collect(Collectors.toList()) -// ) -// .build() -// ); -// } -// } + + private static final Pattern PATTERN = Pattern.compile("@[\\w]+"); + + private final CommentService commentService; + private final PullRequestsService pullRequestsService; + private final PersonService personService; + private final ChangeService changeService; + private final ExecutorScanner executorScanner; + private final TaskService taskService; + private final ConversionService conversionService; + + private final BitbucketProperty bitbucketProperty; + private final CommentSchedulerProperty commentSchedulerProperty; + private final InitProperty initProperty; + + public void scanNewCommentAndTask() { + long commentId = getLastIdCommentOrTask() + 1; + int count = 0; + do { + final List dataScans = generatingLinksToPossibleComments(commentId); + executorScanner.registration(dataScans); + final List resultScans = executorScanner.getResult(); + if (!resultScans.isEmpty()) { + processingComments(resultScans); + processingTasks(resultScans); + count = 0; + } + } while (count++ < commentSchedulerProperty.getNoCommentCount()); + } + + private long getLastIdCommentOrTask() { + Long commentStartId = Long.max(commentService.getLastCommentId(), taskService.getLastTaskId()); + if (commentStartId == 0L && initProperty != null) { + commentStartId = initProperty.getStartCommentId(); + } + return commentStartId; + } + + private void processingComments(List resultScans) { + final List newComments = commentService.createAll(getCommentsByResultScan(resultScans)); + newComments.forEach(this::notificationPersonal); + } + + private void processingTasks(List resultScans) { + final List newTasks = taskService.createAll(getTaskByResultScan(resultScans)); + newTasks.forEach(changeService::createTask); + } + + private List generatingLinksToPossibleComments(@NonNull Long commentId) { + List commentUrls = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + int page = 0; + Sheet pullRequestPage = pullRequestsService.getAll( + PaginationImpl.of(page, commentSchedulerProperty.getCommentCount()) + ); + while (pullRequestPage.hasContent()) { + long finalCommentId = commentId; + commentUrls.addAll(pullRequestPage.getContent().stream() + .map( + pullRequest -> new DataScan( + getCommentUrl(finalCommentId, pullRequest), + pullRequest.getId() + ) + ) + .collect(Collectors.toList())); + pullRequestPage = pullRequestsService.getAll( + PaginationImpl.of(++page, commentSchedulerProperty.getCommentCount()) + ); + } + commentId++; + } + return commentUrls; + } + + private List getCommentsByResultScan(List resultScans) { + return resultScans.stream() + .filter(resultScan -> Severity.NORMAL.equals(resultScan.getCommentJson().getSeverity())) + .map(resultScan -> conversionService.convert(resultScan, Comment.class)) + .collect(Collectors.toList()); + } + + private List getTaskByResultScan(List resultScans) { + return resultScans.stream() + .filter(commentJson -> Severity.BLOCKER.equals(commentJson.getCommentJson().getSeverity())) + .map(resultScan -> conversionService.convert(resultScan, Task.class)) + .collect(Collectors.toList()); + } + + private String getCommentUrl(long commentId, PullRequest pullRequest) { + return bitbucketProperty.getUrlPullRequestComment() + .replace("{projectKey}", pullRequest.getProjectKey()) + .replace("{repositorySlug}", pullRequest.getRepositorySlug()) + .replace("{pullRequestId}", pullRequest.getBitbucketId().toString()) + .replace("{commentId}", String.valueOf(commentId)); + } + + private void notificationPersonal(@NonNull Comment comment) { + Matcher matcher = PATTERN.matcher(comment.getMessage()); + Set recipientsLogins = new HashSet<>(); + while (matcher.find()) { + final String login = matcher.group(0).replace("@", ""); + recipientsLogins.add(login); + } + final Set recipientsIds = personService.getAllTelegramIdByLogin(recipientsLogins); + changeService.createCommentChange( + CommentChange.builder() + .authorName(comment.getAuthor()) + .url(comment.getUrl()) + .telegramIds(recipientsIds) + .message(comment.getMessage()) + .build() + ); + } + + public void scanOldComment() { + @NonNull final List comments = commentService.getAllBetweenDate( + LocalDateTime.now().minusDays(10), LocalDateTime.now() + ); + for (Comment oldComment : comments) { + final Optional optCommentJson = Utils.urlToJson( + oldComment.getUrl(), + bitbucketProperty.getToken(), + CommentJson.class + ); + final Comment newComment = commentService.update(conversionService.convert(oldComment, Comment.class)); + + if (optCommentJson.isPresent()) { + final CommentJson commentJson = optCommentJson.get(); + notifyNewCommentAnswers(oldComment, newComment); + } + } + } + + private void notifyNewCommentAnswers(Comment oldComment, Comment newComment) { + final Set oldAnswerIds = oldComment.getAnswers(); + final Set newAnswerIds = newComment.getAnswers(); + if (!newAnswerIds.isEmpty()) { + final List newAnswers = commentService.getAllById(newAnswerIds).stream() + .filter(comment -> !oldAnswerIds.contains(comment.getId())) + .collect(Collectors.toList()); + changeService.save( + AnswerCommentChange.builder() + .telegramIds( + personService.getAllTelegramIdByLogin(Collections.singleton(newComment.getAuthor())) + ) + .url(newComment.getPullRequestId().toString()) + .youMessage(newComment.getMessage()) + .answers( + newAnswers.stream() + .map(answerComment -> Answer.of(answerComment.getAuthor(), answerComment.getMessage())) + .collect(Collectors.toList()) + ) + .build() + ); + } + } } diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index ea8e9d5..830512f 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -23,7 +23,7 @@ bitbucketbot: no-comment-count: 20 comment-count: 100 init: - start-comment-id: 5947 + start-comment-id: 7623 server-send: url: http://188.225.35.149:8080/api/send bitbucket: diff --git a/src/main/resources/liquibase/v.2.0.0/2020-09-06-create-table.xml b/src/main/resources/liquibase/v.2.0.0/2020-09-06-create-table.xml index 206d0c5..5488bab 100644 --- a/src/main/resources/liquibase/v.2.0.0/2020-09-06-create-table.xml +++ b/src/main/resources/liquibase/v.2.0.0/2020-09-06-create-table.xml @@ -65,17 +65,17 @@ - + - - + @@ -83,21 +83,21 @@ - + - + - - @@ -113,13 +113,13 @@ - - @@ -128,7 +128,7 @@ - + @@ -145,11 +145,11 @@ - - @@ -161,4 +161,18 @@ + + + + + + + + + + + + + \ No newline at end of file