diff --git a/src/main/java/org/upsmf/grievance/model/Ticket.java b/src/main/java/org/upsmf/grievance/model/Ticket.java index 6c70f6d290eac217ed19da15b24f9ea5e97d6d50..bae76f0623e46e8366c57df9f79ad46abcbbe3f5 100644 --- a/src/main/java/org/upsmf/grievance/model/Ticket.java +++ b/src/main/java/org/upsmf/grievance/model/Ticket.java @@ -61,6 +61,9 @@ public class Ticket { @Column(name = "is_escalated") private boolean escalated; + @Column(name = "is_escalated_to_admin") + private boolean escalatedToAdmin; + @Column(name = "escalated_date") private Timestamp escalatedDate; diff --git a/src/main/java/org/upsmf/grievance/model/es/Ticket.java b/src/main/java/org/upsmf/grievance/model/es/Ticket.java index 0f0fd709b1521ee29871bde7cd67475e581238da..e35fad022b8f5d279a24bc9c026bf54c60f4ffd7 100644 --- a/src/main/java/org/upsmf/grievance/model/es/Ticket.java +++ b/src/main/java/org/upsmf/grievance/model/es/Ticket.java @@ -93,4 +93,7 @@ public class Ticket { @Field(name = "rating") private Long rating = 0L; + @Field(name ="is_escalated_to_admin") + private Boolean escalatedToAdmin; + } diff --git a/src/main/java/org/upsmf/grievance/scheduler/NightlyJobScheduler.java b/src/main/java/org/upsmf/grievance/scheduler/NightlyJobScheduler.java index e4a30d62d6410e0531817a542bf07d411baf4cdf..462508192ad63d399234ba8ab3434d29c5ece83c 100644 --- a/src/main/java/org/upsmf/grievance/scheduler/NightlyJobScheduler.java +++ b/src/main/java/org/upsmf/grievance/scheduler/NightlyJobScheduler.java @@ -57,4 +57,12 @@ public class NightlyJobScheduler { emailService.sendSimpleMail(emailDetails); } } + + @Scheduled(cron = "0 0 4 * * ?") + public void escalateTickets(){ + log.info("Starting the escalation job"); + long lastUpdateTimeBeforeEscalation = LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + long response = searchService.escalateTickets(lastUpdateTimeBeforeEscalation); + log.info("No of tickets escalated "+response); + } } diff --git a/src/main/java/org/upsmf/grievance/service/SearchService.java b/src/main/java/org/upsmf/grievance/service/SearchService.java index ced2aa853ddd4676d5742957c82ad6421d98aba4..14b1edef86ff7204592cd5636c641fcc0f573a92 100644 --- a/src/main/java/org/upsmf/grievance/service/SearchService.java +++ b/src/main/java/org/upsmf/grievance/service/SearchService.java @@ -7,6 +7,10 @@ import java.util.Map; public interface SearchService { TicketResponse search(SearchRequest searchRequest); + Map<String, Object> searchTickets(SearchRequest searchRequest); + Map<String, Object> dashboardReport(SearchRequest searchRequest); + + long escalateTickets(Long epochTime); } \ No newline at end of file diff --git a/src/main/java/org/upsmf/grievance/service/TicketService.java b/src/main/java/org/upsmf/grievance/service/TicketService.java index b0b6d27db539b1dbbb72b7aad14053c6ff206951..4c96c7184223776076afe71f23149b1ac2c4dc0a 100644 --- a/src/main/java/org/upsmf/grievance/service/TicketService.java +++ b/src/main/java/org/upsmf/grievance/service/TicketService.java @@ -4,6 +4,8 @@ import org.upsmf.grievance.model.Ticket; import org.upsmf.grievance.dto.TicketRequest; import org.upsmf.grievance.dto.UpdateTicketRequest; +import javax.transaction.Transactional; + public interface TicketService { Ticket save(Ticket ticket); @@ -13,4 +15,7 @@ public interface TicketService { Ticket update(UpdateTicketRequest updateTicketRequest) throws Exception; Ticket getTicketById(long id); + + @Transactional + void updateTicket(Long ticketId); } diff --git a/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java b/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java index b1af8776c5462b4cb768dd45c3d72fe0e6a067f1..1b2043a226067a0ce27bea0c163d54599e95a70b 100644 --- a/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java +++ b/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java @@ -1,6 +1,7 @@ package org.upsmf.grievance.service.impl; import lombok.extern.slf4j.Slf4j; +import org.apache.lucene.search.TotalHits; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.RequestOptions; @@ -11,10 +12,12 @@ import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.task.TaskExecutor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; import org.springframework.stereotype.Service; import org.upsmf.grievance.config.EsConfig; import org.upsmf.grievance.constants.Constants; @@ -26,6 +29,7 @@ import org.upsmf.grievance.model.es.Ticket; import org.upsmf.grievance.model.reponse.TicketResponse; import org.upsmf.grievance.repository.es.TicketRepository; import org.upsmf.grievance.service.SearchService; +import org.upsmf.grievance.service.TicketService; import java.io.IOException; import java.time.Instant; @@ -45,6 +49,9 @@ public class SearchServiceImpl implements SearchService { @Value("${pending.15.days}") private long PENDING_15_DAYS; + @Value("${ticket.escalation.days}") + private long ticketEscalationDays; + @Value("${affiliation}") private String AFFILIATION; @@ -79,6 +86,9 @@ public class SearchServiceImpl implements SearchService { @Autowired private EsConfig esConfig; + @Autowired + private TicketService ticketService; + @Override public TicketResponse search(SearchRequest searchRequest) { //Calculate @@ -154,6 +164,56 @@ public class SearchServiceImpl implements SearchService { return finalResponse; } + @Override + public long escalateTickets(Long lastUpdatedEpoch) { + BoolQueryBuilder finalQuery = createTicketEscalationQuery(lastUpdatedEpoch); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() + .query(finalQuery); + + SearchResponse searchResponse = getSearchResponseFromES(searchSourceBuilder); + if(searchResponse != null) { + TotalHits totalHits = searchResponse.getHits().getTotalHits(); + if(totalHits != null && totalHits.value > 0) { + searchSourceBuilder = new SearchSourceBuilder() + .query(finalQuery).size(Integer.parseInt(String.valueOf(totalHits.value))); + searchResponse = getSearchResponseFromES(searchSourceBuilder); + if(searchResponse != null && searchResponse.getHits()!= null + && searchResponse.getHits().getTotalHits() != null + && searchResponse.getHits().getTotalHits().value > 0) { + escalatePendingTickets(searchResponse); + } + } + } + return searchResponse.getHits().getTotalHits().value; + } + + private void escalatePendingTickets(SearchResponse searchResponse) { + Iterator<SearchHit> hits = searchResponse.getHits().iterator(); + TaskExecutor taskExecutor = new ConcurrentTaskScheduler(); + while(hits.hasNext()) { + Map<String, Object> searchHit = hits.next().getSourceAsMap(); + taskExecutor.execute(new Runnable() { + @Override + public void run() { + ticketService.updateTicket(Long.parseLong(searchHit.get("ticket_id").toString())); + } + }); + } + } + + private SearchResponse getSearchResponseFromES(SearchSourceBuilder searchSourceBuilder) { + SearchResponse searchResponse; + org.elasticsearch.action.search.SearchRequest search = new org.elasticsearch.action.search.SearchRequest("ticket"); + search.searchType(SearchType.QUERY_THEN_FETCH); + search.source(searchSourceBuilder); + try { + searchResponse = esConfig.elasticsearchClient().search(search, RequestOptions.DEFAULT); + } catch (IOException e) { + throw new RuntimeException(e); + } + return searchResponse; + } + private void getfinalResponse(SearchRequest searchRequest, String cc) { SearchResponse searchJunkResponse = getDashboardSearchResponse(searchRequest, "isJunk", cc, null); SearchResponse searchOpenStatusResponse = getDashboardSearchResponse(searchRequest, "openStatus", cc, null); @@ -259,14 +319,7 @@ public class SearchServiceImpl implements SearchService { SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() .query(finalQuery); - org.elasticsearch.action.search.SearchRequest search = new org.elasticsearch.action.search.SearchRequest("ticket"); - search.searchType(SearchType.QUERY_THEN_FETCH); - search.source(searchSourceBuilder); - try { - searchResponse = esConfig.elasticsearchClient().search(search, RequestOptions.DEFAULT); - } catch (IOException e) { - throw new RuntimeException(e); - } + searchResponse = getSearchResponseFromES(searchSourceBuilder); return searchResponse; } @@ -537,6 +590,18 @@ public class SearchServiceImpl implements SearchService { return finalQuery; } + private BoolQueryBuilder createTicketEscalationQuery(Long lastUpdatedEpoch) { + BoolQueryBuilder finalQuery = QueryBuilders.boolQuery(); + // search query + RangeQueryBuilder createdDateKeywordMatchQuery = QueryBuilders.rangeQuery("updated_date_ts").lte(lastUpdatedEpoch); + MatchQueryBuilder escalatedMatchQuery = QueryBuilders.matchQuery("is_escalated", false); + MatchQueryBuilder statusMatchQuery = QueryBuilders.matchQuery("status", "OPEN"); + BoolQueryBuilder keywordSearchQuery = QueryBuilders.boolQuery(); + keywordSearchQuery.must(createdDateKeywordMatchQuery).must(escalatedMatchQuery).must(statusMatchQuery); + finalQuery.must(keywordSearchQuery); + return finalQuery; + } + private BoolQueryBuilder getPriority(String priority, BoolQueryBuilder finalQuery) { if (priority !=null && !priority.isBlank()) { MatchQueryBuilder priorityMatchQuery = QueryBuilders.matchQuery("priority", priority); diff --git a/src/main/java/org/upsmf/grievance/service/impl/TicketServiceImpl.java b/src/main/java/org/upsmf/grievance/service/impl/TicketServiceImpl.java index 5d7d3012495a60ae15c92dd8023f85242b893036..7160a965b8d01c6a7c5cc7452215e3443d8e705e 100644 --- a/src/main/java/org/upsmf/grievance/service/impl/TicketServiceImpl.java +++ b/src/main/java/org/upsmf/grievance/service/impl/TicketServiceImpl.java @@ -2,6 +2,7 @@ package org.upsmf.grievance.service.impl; import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.util.StringUtils; +import lombok.Synchronized; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -26,6 +27,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.regex.Pattern; @@ -309,6 +311,7 @@ public class TicketServiceImpl implements TicketService { .priority(ticket.getPriority()) .escalatedBy(ticket.getEscalatedBy()) .escalatedTo(ticket.getEscalatedTo()) + .escalatedToAdmin(ticket.isEscalatedToAdmin()) .rating(Long.valueOf(0)).build(); } @@ -382,7 +385,26 @@ public class TicketServiceImpl implements TicketService { if (request.getPriority() == null || !isValidPriority(request.getPriority())) { throw new IllegalArgumentException("Invalid ticket priority"); } + } - + @Override + @Transactional + @Synchronized + public void updateTicket(Long ticketId) { + Ticket ticket = getTicketById(ticketId); + ticket.setUpdatedDate(new Timestamp(new Date().getTime())); + ticket.setEscalatedDate(new Timestamp(new Date().getTime())); + ticket.setEscalatedToAdmin(true); + ticketRepository.save(ticket); + ticket = getTicketById(ticket.getId()); + // check if ticket exists in ES + Optional<org.upsmf.grievance.model.es.Ticket> esTicketDetails = esTicketRepository.findOneByTicketId(ticket.getId()); + org.upsmf.grievance.model.es.Ticket updatedESTicket = convertToESTicketObj(ticket); + if(esTicketDetails.isPresent()) { + // TODO revisit this + esTicketRepository.deleteById(esTicketDetails.get().getId()); + updatedESTicket.setRating(esTicketDetails.get().getRating()); + } + org.upsmf.grievance.model.es.Ticket curentUpdatedTicket=esTicketRepository.save(updatedESTicket); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6bc396a8cb9b3b178fc493653317885dbbf7ebb1..d4f997907dd4ea9d500e2dcbe35ee7654f52093a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -53,7 +53,7 @@ spring.servlet.multipart.max-request-size=10MB # Dashboard pending.21.days=21 pending.15.days=15 -ticket.escalation.days=14 +ticket.escalation.days=7 email.ids=nodalofficer@yopmail.com,grievance@yopmail.com subject.daily.report=Daily Report subject.bi.weekly.report=Bi Weekly Report