From 36b84fd1f6ea8016ca5ea160dd6ba8bae72ff88e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Petrovi=C4=8D?= <mifo.petrovic@gmail.com>
Date: Tue, 12 Mar 2024 01:40:07 +0100
Subject: [PATCH] pagination and lazy load sequences

---
 .../analyse/cpg/CpgController.java            | 47 ++++++++++++++++++-
 .../dnaAnalyser/analyse/cpg/CpgMapper.java    | 21 +++++++++
 .../dnaAnalyser/analyse/cpg/CpgService.java   | 28 +++++++++--
 3 files changed, 91 insertions(+), 5 deletions(-)

diff --git a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgController.java b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgController.java
index b32d3d0..f29228e 100644
--- a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgController.java
+++ b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgController.java
@@ -3,8 +3,11 @@ package cz.mendelu.dnaAnalyser.analyse.cpg;
 import cz.mendelu.dnaAnalyser.analyse.zdna.ZdnaAnalyseResult;
 import cz.mendelu.dnaAnalyser.jwt.JwtAuthenticationUtils;
 import cz.mendelu.dnaAnalyser.jwt.JwtTokenService;
+import cz.mendelu.dnaAnalyser.sequence.Sequence;
 import cz.mendelu.dnaAnalyser.sequence.SequenceService;
 import cz.mendelu.dnaAnalyser.exporter.ExporterService;
+import cz.mendelu.dnaAnalyser.sequence.data.SequenceData;
+import cz.mendelu.dnaAnalyser.sequence.data.SequenceDataRepository;
 import cz.mendelu.dnaAnalyser.utils.analyse.service.heatmap.Heatmap;
 import cz.mendelu.dnaAnalyser.utils.controller.DeleteMapping;
 import cz.mendelu.dnaAnalyser.utils.controller.GetMapping;
@@ -77,6 +80,9 @@ public class CpgController {
     @Autowired
     private JwtTokenService jwtTokenService;
 
+    @Autowired
+    private SequenceDataRepository sequenceDataRepository;
+
     @Autowired
     private CpgService cpgService;
 
@@ -100,6 +106,38 @@ public class CpgController {
         return Response.response(cpg);
     }
 
+    @GetMapping("/{id}/substring")
+    @ResponseStatus(HttpStatus.OK)
+    @PreAuthorize("isAuthenticated()")
+    @ApiOperation("Get a substring of the sequence by ID, start, and end positions.")
+    public ResponseEntity<String> getSequenceSubstring(
+            @PathVariable UUID id,
+            @RequestParam("start") Integer start,
+            @RequestParam("end") Integer end) {
+
+        // Fetch the sequence using the provided ID. You might need to adjust this code
+        // based on how your SequenceService is implemented and how sequences are stored.
+        Sequence sequence = sequenceService.findOne(id);
+        if (sequence == null) {
+            return new ResponseEntity<>("Sequence not found", HttpStatus.NOT_FOUND);
+        }
+
+
+        String sequence_string = sequenceDataRepository.load(sequence).toPlain();
+
+        // Check if start and end positions are within the sequence length
+        if (start < 0 || end > sequence_string.length() || start >= end) {
+            return new ResponseEntity<>("Invalid start or end position", HttpStatus.BAD_REQUEST);
+        }
+
+        // Extract the substring based on the provided start and end positions
+        String substring = sequence_string.substring(start, end);
+
+        // Return the substring
+        return new ResponseEntity<>(substring, HttpStatus.OK);
+    }
+
+
     @GetMapping("/{id}/analysis")
     @ResponseStatus(HttpStatus.OK)
     @PreAuthorize("isAuthenticated()")
@@ -115,8 +153,13 @@ public class CpgController {
     @ResponseStatus(HttpStatus.OK)
     @PreAuthorize("isAuthenticated()")
     @ApiOperation("Get CpG islands analysis by ID.")
-    public ResponsePage<CpgAnalyseResult> getCpgList(@PathVariable UUID id) {
-        Page<CpgAnalyseResult> cpgPage = cpgService.returnCpgAnalysis(id);
+    public ResponsePage<CpgAnalyseResult> getCpgList(@PathVariable UUID id,
+                                                     Integer sequenceStart,
+                                                     Integer sequenceLength,
+                                                     PaginationParam paginationParam,
+                                                     SortParam sortParam) {
+        Pageable pageable = paginationParam.toPageable(sortParam.toSort());
+        Page<CpgAnalyseResult> cpgPage = cpgService.returnCpgAnalysis(id, sequenceStart, sequenceLength, pageable);
         return ResponsePage.responsePage(cpgPage);
     }
 
diff --git a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgMapper.java b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgMapper.java
index 66cca01..30ce4e7 100644
--- a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgMapper.java
+++ b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgMapper.java
@@ -25,6 +25,11 @@ public interface CpgMapper extends DataLocalisedMapper<CpgAnalyseResult> {
     @Select("SELECT count(*) FROM CPG")
     int countAll();
 
+    @Select("SELECT count(*) FROM CPG WHERE POSITION >= #{startPosition} AND POSITION < #{endPosition}")
+    int countAllBetweenPosition(
+            @Param("startPosition") int startPosition,
+            @Param("endPosition") int endPosition);
+
     @Select("SELECT * FROM CPG")
     @Results(value = {
             @Result(property = "id", column = "ID"),
@@ -99,6 +104,22 @@ public interface CpgMapper extends DataLocalisedMapper<CpgAnalyseResult> {
             @Param("orderName") String orderName,
             RowBounds rowBounds);
 
+    @Select("SELECT * FROM CPG WHERE POSITION >= #{startPosition} AND POSITION < #{endPosition} ORDER BY ${orderName} DESC")
+    @Results(value = {
+            @Result(property = "id", column = "ID"),
+            @Result(property = "position", column = "position"),
+            @Result(property = "end", column = "end"),
+            @Result(property = "sequence", column = "sequence"),
+            @Result(property = "gcPerc", column = "gcPerc"),
+            @Result(property = "observedToExpectedCpG", column = "observedToExpectedCpG"),
+            @Result(property = "length", column = "length")
+    })
+    List<CpgAnalyseResult> getAllBetweenPositionOrderDesc(
+            @Param("startPosition") int startPosition,
+            @Param("endPosition") int endPosition,
+            @Param("orderName") String orderName,
+            RowBounds rowBounds);
+
     @Delete("DELETE from CPG WHERE ID = #{id}")
     void delete(int id);
 
diff --git a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgService.java b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgService.java
index 895d24c..6c012e8 100644
--- a/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgService.java
+++ b/src/main/java/cz/mendelu/dnaAnalyser/analyse/cpg/CpgService.java
@@ -87,15 +87,37 @@ public class CpgService extends ModelService<Cpg> {
         return cpg;
     }
 
-    public Page<CpgAnalyseResult> returnCpgAnalysis(UUID id) {
+    public Page<CpgAnalyseResult> returnCpgAnalysis(UUID id, Integer sequenceStart, Integer sequenceLength, Pageable pageable) {
         Cpg analysis = findOne(id);
+
+        int start = (sequenceStart == null) ? 0 : sequenceStart;
+        int end = start + ((sequenceLength == null) ? analysis.getSequence().getLength() : sequenceLength);
+
         try (SqlSession sqlSession = cpgDataSessionService.openDataSession(analysis)) {
             CpgMapper cpgMapper = sqlSession.getMapper(CpgMapper.class);
-            Page<CpgAnalyseResult> cpgPage = new PageImpl<>(cpgMapper.getAll());
-            return cpgPage;
+
+            RowBounds rowBounds = new RowBounds((int) pageable.getOffset(), pageable.getPageSize());
+            String orderName = "POSITION";
+            Sort.Direction orderDirection = Sort.Direction.ASC;
+            Sort.Order order = getSortOrderOrNull(pageable);
+            if (order != null) {
+                orderName = order.getProperty().toUpperCase();
+                orderDirection = order.getDirection();
+            }
+
+            List<CpgAnalyseResult> list;
+            if (orderDirection == Sort.Direction.ASC) {
+                list = cpgMapper.getAllBetweenPositionOrderAsc(start, end, orderName, rowBounds);
+            } else {
+                list = cpgMapper.getAllBetweenPositionOrderDesc(start, end, orderName, rowBounds);
+            }
+            int total = cpgMapper.countAllBetweenPosition(start, end);
+
+            return new PageImpl<>(list, pageable, total);
         }
     }
 
+
     public Heatmap getHeatmap(UUID id, Integer segmentsCount, Integer from, Integer to) {
         Cpg analysis = findOne(id);
         return cpgDataSessionService.heatmap(CpgMapper.class, analysis, segmentsCount, from, to);
-- 
GitLab