Browse Source

Ya se pueden filtrat por rango de fechas los tickets

main
mramirezg 2 months ago
parent
commit
f9ee7092ad
3 changed files with 126 additions and 39 deletions
  1. +63
    -12
      src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java
  2. +16
    -7
      src/main/java/mx/gob/jumapacelaya/models/Ticket.java
  3. +47
    -20
      src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java

+ 63
- 12
src/main/java/mx/gob/jumapacelaya/api/RedmineClient.java View File

@ -39,42 +39,72 @@ public class RedmineClient {
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
public List<Ticket> getTickets(RedmineUser user, boolean includeClosed, int offset, int limit) {
public List<Ticket> getTickets(RedmineUser user, boolean includeClosed,
int offset, int limit,
LocalDate fechaDesde,
LocalDate fechaHasta) {
List<Ticket> tickets = new ArrayList<>();
HttpClient client = HttpClient.newHttpClient();
// Muestra solamnete los que estan cerrados
String statusFilter = includeClosed ? "&status_id=closed" : "&status_id=open";
// Filtrar tickets abiertos o cerrados
String statusFilter = includeClosed ? "&status_id=open" : "&status_id=closed";
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&offset=" + offset + "&limit=" + limit);
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&offset=" + offset + "&limit=" + limit))
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
.build();
System.out.println(request.toString());
try {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
String responseBody = response.body();
//log.info(responseBody);
tickets.addAll(parseTickets(responseBody));
tickets.addAll(parseTickets(response.body()));
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
}
return tickets;
}
public int getTotalTickets(RedmineUser user,
boolean includeClosed,
LocalDate fechaDesde,
LocalDate fechaHasta) {
public int getTotalTickets(RedmineUser user, boolean includedClose) {
HttpClient client = HttpClient.newHttpClient();
String statusFilter = includedClose ? "&status_id=closed" : "&status_id=open";
// Filtrar tickets abiertos o cerrados
String statusFilter = includeClosed ? "&status_id=open" : "&status_id=closed";
StringBuilder url = new StringBuilder(REDMINE_URL + "/issues.json?key=" + user.getKey()
+ statusFilter + "&limit=1");
// Filtro por fechas
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
if (fechaDesde != null) {
url.append("&created_on=%3E%3D").append(fechaDesde.format(formatter));
}
if (fechaHasta != null) {
url.append("&created_on=%3C%3D").append(fechaHasta.format(formatter));
}
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + statusFilter + "&limit=1"))
.uri(URI.create(url.toString()))
.header("Content-Type", "application/json")
.build();
@ -83,6 +113,8 @@ public class RedmineClient {
if (response.statusCode() == 200) {
JsonObject jsonObject = JsonParser.parseString(response.body()).getAsJsonObject();
return jsonObject.get("total_count").getAsInt();
} else {
System.err.println("Error en la respuesta: " + response.statusCode());
}
} catch (Exception e) {
e.printStackTrace();
@ -90,6 +122,7 @@ public class RedmineClient {
return 0;
}
//AQUI OBTENGO LOS TICKETS DESDE REDMINE
/*public List<Ticket> getTicketsAuthor(RedmineUser user, boolean includeClosed) {
List<Ticket> tickets = new ArrayList<>();
@ -383,4 +416,22 @@ public class RedmineClient {
return null;
}
}
public List<Ticket> getAllTickets(RedmineUser user, boolean abiertos,
LocalDate fechaDesde, LocalDate fechaHasta) {
List<Ticket> allTickets = new ArrayList<>();
int offset = 0;
int limit = 100; // Redmine suele permitir hasta 100 por request (puedes probar 1000)
List<Ticket> batch;
do {
batch = getTickets(user, abiertos, offset, limit, fechaDesde, fechaHasta);
allTickets.addAll(batch);
offset += limit;
} while (!batch.isEmpty());
return allTickets;
}
}

+ 16
- 7
src/main/java/mx/gob/jumapacelaya/models/Ticket.java View File

@ -6,6 +6,7 @@ import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class Ticket {
private final int id;
@ -50,14 +51,22 @@ public class Ticket {
if (dateStr == null || dateStr.isEmpty()) {
return null;
}
List<DateTimeFormatter> formatters = List.of(
DateTimeFormatter.ISO_OFFSET_DATE_TIME,
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm")
);
for (DateTimeFormatter formatter : formatters) {
try {
return LocalDateTime.parse(dateStr, formatter);
} catch (Exception ignored) {}
}
try {
if (dateStr.endsWith("Z")) {
return OffsetDateTime.parse(dateStr)
.atZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
} else {
return LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
}
LocalDate ld = LocalDate.parse(dateStr);
return ld.atStartOfDay();
} catch (Exception e) {
System.err.println("Error al parsear fecha: " + dateStr);
return null;


+ 47
- 20
src/main/java/mx/gob/jumapacelaya/ui/ActDiariaView.java View File

@ -121,6 +121,7 @@ public class ActDiariaView extends VerticalLayout {
fechaDesde.setEnabled(true);
fechaHasta.setEnabled(true);
btnBuscar.setEnabled(true);
btnBuscar.addClickListener(e -> filtrarTickets());
// Cargar tickets iniciales (cerrados)
loadTickets(false);
@ -283,18 +284,26 @@ public class ActDiariaView extends VerticalLayout {
grid.setItems(
query -> {
int offset = query.getOffset();
int limit = query.getLimit();
try {
List<Ticket> ticketsPage = redmineClient.getTickets(user, paramCliente, offset, limit);
return ticketsPage.stream();
return redmineClient.getTickets(
user,
chkSoloAbiertos.getValue(),
query.getOffset(),
query.getLimit(),
fechaDesde.getValue(),
fechaHasta.getValue()
).stream();
} catch (Exception e) {
e.printStackTrace();
return Stream.empty();
}
},
query -> redmineClient.getTotalTickets(user, paramCliente)
query -> redmineClient.getTotalTickets(
user,
chkSoloAbiertos.getValue(),
fechaDesde.getValue(),
fechaHasta.getValue()
)
);
}
@ -393,12 +402,19 @@ public class ActDiariaView extends VerticalLayout {
try {
RedmineUser user = userService.getRedmineUser();
List<Ticket> ticketsCerrados = redmineClient.getTickets(user, true, 0, 10000);
// Traer TODOS los tickets cerrados, respetando fechas si están seleccionadas
List<Ticket> ticketsCerrados = redmineClient.getAllTickets(
user,
false, // false = cerrados
fechaDesde.getValue(),
fechaHasta.getValue()
);
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Tickets cerrados");
String[] headers = {"ID","Tipo","Estado","Autor","Asunto","Fecha Creación","Fecha Cierre","Fecha actualización","Duración","Situación"};
String[] headers = {"ID","Tipo","Tiempo estimado","Fecha creación","Fecha cierre",
"Fecha actualización","Situación","Estado","Duración","Autor","Asunto"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
@ -412,25 +428,26 @@ public class ActDiariaView extends VerticalLayout {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(ticket.getId());
row.createCell(1).setCellValue(ticket.getType());
row.createCell(2).setCellValue(ticket.getStatus());
row.createCell(3).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(4).setCellValue(ticket.getSubject());
row.createCell(5).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(6).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(2).setCellValue(ticket.getEstimatedHrs() + " horas");
row.createCell(3).setCellValue(ticket.getDateCreate() != null ? ticket.getDateCreate().format(formatter) : "");
row.createCell(4).setCellValue(ticket.getDateClose() != null ? ticket.getDateClose().format(formatter) : "");
row.createCell(5).setCellValue(ticket.getUpdateOn() != null ? ticket.getUpdateOn().format(formatter) : "");
row.createCell(7).setCellValue(ticket.getStatus());
row.createCell(9).setCellValue(ticket.getAuthor() != null ? ticket.getAuthor().getUsername() : "");
row.createCell(10).setCellValue(ticket.getSubject());
if (ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long dias = ChronoUnit.DAYS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(8).setCellValue(dias + " dias");
row.createCell(8).setCellValue(dias + " días");
} else {
row.createCell(8).setCellValue("");
}
if (ticket.getEstimatedHrs() != null && ticket.getDateCreateLocal() != null && ticket.getDateCloseLocal() != null) {
long horasReales = ChronoUnit.HOURS.between(ticket.getDateCreateLocal(), ticket.getDateCloseLocal());
row.createCell(9).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
row.createCell(6).setCellValue(horasReales <= ticket.getEstimatedHrs() ? "En tiempo" : "Excedido");
} else {
row.createCell(9).setCellValue("Sin estimación");
row.createCell(6).setCellValue("Sin estimación");
}
}
@ -442,10 +459,14 @@ public class ActDiariaView extends VerticalLayout {
workbook.write(out);
workbook.close();
StreamResource resource = new StreamResource("tickets_cerrados.xlsx", () -> new ByteArrayInputStream(out.toByteArray()));
StreamResource resource = new StreamResource("tickets_cerrados.xlsx",
() -> new ByteArrayInputStream(out.toByteArray()));
StreamRegistration registration = UI.getCurrent().getSession().getResourceRegistry().registerResource(resource);
UI.getCurrent().getPage().executeJs("window.open('" + registration.getResourceUri().toString() + "','_blank')");
StreamRegistration registration = UI.getCurrent().getSession()
.getResourceRegistry().registerResource(resource);
UI.getCurrent().getPage().executeJs(
"window.open('" + registration.getResourceUri().toString() + "','_blank')"
);
} catch (Exception e) {
log.error("Error exportando tickets", e);
@ -453,4 +474,10 @@ public class ActDiariaView extends VerticalLayout {
.addThemeVariants(NotificationVariant.LUMO_ERROR);
}
}
private void filtrarTickets() {
boolean soloAbiertos = chkSoloAbiertos.getValue();
loadTickets(soloAbiertos);
}
}

Loading…
Cancel
Save