From 1a3dd7415e928942e58b103c93c662f5f1168abe Mon Sep 17 00:00:00 2001 From: juliofreitaass Date: Fri, 23 May 2025 06:44:21 -0300 Subject: [PATCH 1/2] SCRUM-115-Criar-endpoint-para-exportar-os-dados-em-arquivos-CSV(feat):created method service to extract csv --- stratify/pom.xml | 6 +- .../stratify/config/SpringSecurityConfig.java | 5 +- .../stratify/services/CsvDownloadService.java | 116 ++++++++++++++++++ .../controllers/CsvDownloadController.java | 62 ++++++++++ .../stratify/services/CsvDownloadTest.java | 73 +++++++++++ 5 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java create mode 100644 stratify/src/main/java/com/quantum/stratify/web/controllers/CsvDownloadController.java create mode 100644 stratify/src/test/java/com/quantum/stratify/services/CsvDownloadTest.java diff --git a/stratify/pom.xml b/stratify/pom.xml index bc2a1b6..01a4ffe 100644 --- a/stratify/pom.xml +++ b/stratify/pom.xml @@ -135,7 +135,11 @@ org.springframework.boot spring-boot-starter-validation - + + com.opencsv + opencsv + 5.7.1 + org.springframework.boot spring-boot-starter-mail diff --git a/stratify/src/main/java/com/quantum/stratify/config/SpringSecurityConfig.java b/stratify/src/main/java/com/quantum/stratify/config/SpringSecurityConfig.java index 341c43b..397e03d 100644 --- a/stratify/src/main/java/com/quantum/stratify/config/SpringSecurityConfig.java +++ b/stratify/src/main/java/com/quantum/stratify/config/SpringSecurityConfig.java @@ -1,8 +1,6 @@ package com.quantum.stratify.config; -import com.quantum.stratify.config.jwt.JwtAuthenticationEntryPoint; -import com.quantum.stratify.config.jwt.JwtAuthorizationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -17,6 +15,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import com.quantum.stratify.config.jwt.JwtAuthenticationEntryPoint; +import com.quantum.stratify.config.jwt.JwtAuthorizationFilter; + @EnableMethodSecurity @EnableWebMvc diff --git a/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java b/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java new file mode 100644 index 0000000..ef040b3 --- /dev/null +++ b/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java @@ -0,0 +1,116 @@ +package com.quantum.stratify.services; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.stereotype.Service; + +import com.opencsv.CSVWriter; +import com.quantum.stratify.web.dtos.PercentualStatusUsuarioDTO; +import com.quantum.stratify.web.dtos.QuantidadeCardsPorSprintDTO; +import com.quantum.stratify.web.dtos.QuantidadeCardsPorTagDTO; +import com.quantum.stratify.web.dtos.ResponseQuantidadeCardsByPeriodo; +import com.quantum.stratify.web.dtos.TempoMedioPorProjetoDTO; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CsvDownloadService { + + @Autowired + private UserStoryService userStoryService; + + @Autowired + private FatoUserStoryTemporaisService fatoUserStoryTemporaisService; + + @Autowired + private UserStoryTagService userStoryTagService; + + @Autowired + private UserStoryStatusService userStoryStatusService; + + @Autowired + private FatoEficienciaUserStoryService fatoEficienciaUserStoryService; + public InputStreamResource generateCsv(Long projectId, Long userId, String exportType) { + List csvData; + + switch (exportType.toLowerCase()) { + case "cardsporsprint": { + List lista = userStoryService.getQuantidadeUserStoriesBySprint(projectId, userId); + csvData = new ArrayList<>(); + csvData.add(new String[]{"Sprint", "Quantidade"}); + for (QuantidadeCardsPorSprintDTO dto : lista) { + csvData.add(new String[]{dto.getSprint(), String.valueOf(dto.getQuantidade())}); + } + log.info("ExportType=cardsporsprint → Encontrados {} registros para projeto={} usuário={}", lista.size(), projectId, userId); + break; + } + case "cardsporperiodo": { + List lista = fatoUserStoryTemporaisService.getUserStoriesByPeriodoAndUser(projectId, userId); + csvData = new ArrayList<>(); + csvData.add(new String[]{"Período", "Criadas", "Finalizadas"}); + for (ResponseQuantidadeCardsByPeriodo dto : lista) { + csvData.add(new String[]{ + dto.periodo(), + String.valueOf(dto.quantidadeCriadas()), + String.valueOf(dto.quantidadeFinalizadas()) + }); + } + break; + } + case "cardsporetiqueta": { + List lista = userStoryTagService.getQuantidadeUserStoriesByTag(projectId, userId); + csvData = new ArrayList<>(); + csvData.add(new String[]{"Tag", "Quantidade"}); + for (QuantidadeCardsPorTagDTO dto : lista) { + csvData.add(new String[]{dto.getNomeTag(), String.valueOf(dto.getQuantidade())}); + } + break; + } + case "cardsporstatus": { + List lista = userStoryStatusService.getPercentualUserStoriesPorStatus(projectId, userId); + csvData = new ArrayList<>(); + csvData.add(new String[]{"Status", "Percentual"}); + for (PercentualStatusUsuarioDTO dto : lista) { + csvData.add(new String[]{dto.getNomeStatus(), String.valueOf(dto.getPercentual())}); + } + break; + } + case "tempomedio": { + List lista = fatoEficienciaUserStoryService.getTempoMedioFiltrado(projectId, userId); + csvData = new ArrayList<>(); + csvData.add(new String[]{"ID User Story", "Descrição", "Tempo Médio"}); + for (TempoMedioPorProjetoDTO dto : lista) { + csvData.add(new String[]{ + String.valueOf(dto.idUserStory()), + dto.descricao(), + String.valueOf(dto.tempoMedio()) + }); + } + break; + } + default: + throw new IllegalArgumentException("Tipo de exportação inválido: " + exportType); + } + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (CSVWriter writer = new CSVWriter(new OutputStreamWriter(out))) { + for (String[] line : csvData) { + writer.writeNext(line); + } + } catch (IOException e) { + throw new RuntimeException("Erro ao gerar CSV", e); + } + + return new InputStreamResource(new ByteArrayInputStream(out.toByteArray())); + } +} diff --git a/stratify/src/main/java/com/quantum/stratify/web/controllers/CsvDownloadController.java b/stratify/src/main/java/com/quantum/stratify/web/controllers/CsvDownloadController.java new file mode 100644 index 0000000..676a3ed --- /dev/null +++ b/stratify/src/main/java/com/quantum/stratify/web/controllers/CsvDownloadController.java @@ -0,0 +1,62 @@ +package com.quantum.stratify.web.controllers; + +import java.io.IOException; + +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.quantum.stratify.services.CsvDownloadService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/download") +@CrossOrigin("*") +@Slf4j +@Tag(name = "CSV Download", description = "Exportação de relatórios em formato CSV") +public class CsvDownloadController { + + private final CsvDownloadService csvDownloadService; + + @Operation(summary = "Exporta dados filtrados em formato CSV") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Arquivo CSV gerado com sucesso"), + @ApiResponse(responseCode = "400", description = "Parâmetros inválidos"), + @ApiResponse(responseCode = "404", description = "Dados não encontrados"), + @ApiResponse(responseCode = "500", description = "Erro interno ao gerar o CSV") + }) + @GetMapping("/csv") + public ResponseEntity downloadCsv( + @Parameter(description = "Tipo de exportação (ex: cardsporsprint, cardsporperiodo, etc.)", required = true) + @RequestParam String exportType, + + @Parameter(description = "ID do projeto (opcional)") + @RequestParam(required = false) Long projectId, + + @Parameter(description = "ID do usuário (opcional)") + @RequestParam(required = false) Long userId + ) throws IOException { + + String filename = exportType + ".csv"; + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename); + headers.add(HttpHeaders.CONTENT_TYPE, "text/csv"); + + InputStreamResource resource = csvDownloadService.generateCsv(projectId, userId, exportType); + return new ResponseEntity<>(resource, headers, HttpStatus.OK); + } +} diff --git a/stratify/src/test/java/com/quantum/stratify/services/CsvDownloadTest.java b/stratify/src/test/java/com/quantum/stratify/services/CsvDownloadTest.java new file mode 100644 index 0000000..9a00b4f --- /dev/null +++ b/stratify/src/test/java/com/quantum/stratify/services/CsvDownloadTest.java @@ -0,0 +1,73 @@ +package com.quantum.stratify.services; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.core.io.InputStreamResource; + +import com.quantum.stratify.web.dtos.QuantidadeCardsPorSprintDTO; +import com.quantum.stratify.web.dtos.ResponseQuantidadeCardsByPeriodo; + +class CsvDownloadServiceTest { + + @Mock + private UserStoryService userStoryService; + @Mock + private FatoUserStoryTemporaisService fatoUserStoryTemporaisService; + @Mock + private UserStoryTagService userStoryTagService; + @Mock + private UserStoryStatusService userStoryStatusService; + @Mock + private FatoEficienciaUserStoryService fatoEficienciaUserStoryService; + + @InjectMocks + private CsvDownloadService csvDownloadService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void shouldGenerateCsvForCardsporsprint() throws Exception { + when(userStoryService.getQuantidadeUserStoriesBySprint(1L, 1L)).thenReturn( + List.of(new QuantidadeCardsPorSprintDTO("Sprint 1", 5L)) + ); + + InputStreamResource result = csvDownloadService.generateCsv(1L, 1L, "cardsporsprint"); + BufferedReader reader = new BufferedReader(new InputStreamReader(result.getInputStream())); + + assertEquals("\"Sprint\",\"Quantidade\"", reader.readLine()); + assertEquals("\"Sprint 1\",\"5\"", reader.readLine()); + } + + @Test + void shouldGenerateCsvForCardsporperiodo() throws Exception { + when(fatoUserStoryTemporaisService.getUserStoriesByPeriodoAndUser(1L, 1L)).thenReturn( + List.of(new ResponseQuantidadeCardsByPeriodo("2024-01", 3, 2)) + ); + + InputStreamResource result = csvDownloadService.generateCsv(1L, 1L, "cardsporperiodo"); + BufferedReader reader = new BufferedReader(new InputStreamReader(result.getInputStream())); + + assertEquals("\"Período\",\"Criadas\",\"Finalizadas\"", reader.readLine()); + assertEquals("\"2024-01\",\"3\",\"2\"", reader.readLine()); + } + + @Test + void shouldThrowForInvalidExportType() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> csvDownloadService.generateCsv(1L, 1L, "invalido")); + assertEquals("Tipo de exportação inválido: invalido", ex.getMessage()); + } +} From e6e71e07bd5e35783be5b4820832b54c9a06c5f9 Mon Sep 17 00:00:00 2001 From: juliofreitaass Date: Fri, 23 May 2025 07:21:42 -0300 Subject: [PATCH 2/2] SCRUM-115(feat):add quantidaderetrabalho column --- .../com/quantum/stratify/services/CsvDownloadService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java b/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java index ef040b3..8294997 100644 --- a/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java +++ b/stratify/src/main/java/com/quantum/stratify/services/CsvDownloadService.java @@ -88,12 +88,13 @@ public InputStreamResource generateCsv(Long projectId, Long userId, String expor case "tempomedio": { List lista = fatoEficienciaUserStoryService.getTempoMedioFiltrado(projectId, userId); csvData = new ArrayList<>(); - csvData.add(new String[]{"ID User Story", "Descrição", "Tempo Médio"}); + csvData.add(new String[]{"ID User Story", "Descrição", "Tempo Médio", "Quantidade Retrabalho"}); for (TempoMedioPorProjetoDTO dto : lista) { csvData.add(new String[]{ String.valueOf(dto.idUserStory()), dto.descricao(), - String.valueOf(dto.tempoMedio()) + String.valueOf(dto.tempoMedio()), + String.valueOf(dto.quantidadeRetrabalhos()) }); } break;