From 5db76afea3445268738cac8364827df81b85bc2f Mon Sep 17 00:00:00 2001
From: Shishir Suman <shishir.suman@tarento.com>
Date: Thu, 5 Oct 2023 14:09:02 +0530
Subject: [PATCH] fixes to creating scheduler to escalate tickets

---
 .../org/upsmf/grievance/model/Ticket.java     |  3 +
 .../org/upsmf/grievance/model/es/Ticket.java  |  3 +
 .../scheduler/NightlyJobScheduler.java        |  8 ++
 .../grievance/service/SearchService.java      |  4 +
 .../grievance/service/TicketService.java      |  5 ++
 .../service/impl/SearchServiceImpl.java       | 81 +++++++++++++++++--
 .../service/impl/TicketServiceImpl.java       | 24 +++++-
 src/main/resources/application.properties     |  2 +-
 8 files changed, 120 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/upsmf/grievance/model/Ticket.java b/src/main/java/org/upsmf/grievance/model/Ticket.java
index 6c70f6d..bae76f0 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 0f0fd70..e35fad0 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 e4a30d6..4625081 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 ced2aa8..14b1ede 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 b0b6d27..4c96c71 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 b1af877..1b2043a 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 5d7d301..7160a96 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 6bc396a..d4f9979 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 
-- 
GitLab