package com.alexanderlogistics.metrics; import java.util.List; import java.util.logging.Logger; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; import com.alexanderlogistics.InvoiceUtil; import jakarta.ejb.Stateless; import jakarta.ejb.TransactionAttribute; import jakarta.ejb.TransactionAttributeType; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @Path("metrics/dbtr") @Stateless public class MetricDebitorRestService { private static Logger logger = Logger.getLogger(MetricDebitorRestService.class.getName()); @Inject DocumentService documentService; @Inject MetricDebitorService metricDebitorService; @Inject MetricDataService metricDataService; @GET @Path("/ping") @Produces({ MediaType.TEXT_PLAIN }) public String ping() { logger.info("GET ping"); return "ping: " + System.currentTimeMillis(); } /** * This method initializes the metrics for all debitors with open invoices. * * The method first deletes all existing metrics and than creates or updates the * metric entires for each debitor. * * @return */ @GET @Path("/rebuild") @Produces({ MediaType.TEXT_PLAIN }) public Response rebuildMetrics() { StringBuffer messageBuffer = new StringBuffer(); long l = System.currentTimeMillis(); log("├── rebuild dbtr metrics...", messageBuffer); try { log("│   ├── delete all metrics", messageBuffer); // first clear the metric cache metricDebitorService.reset(); log("│   ├── reset metric cache", messageBuffer); computeMetrics(messageBuffer); String message = "├── rebuild dbtr metrics completed in " + (System.currentTimeMillis() - l) + "ms"; log(message, messageBuffer); return Response.ok().entity(messageBuffer.toString()).build(); } catch (Exception e) { e.printStackTrace(); log("Failed to initialize metrics: " + e.getMessage(), messageBuffer); return Response.serverError() .entity(messageBuffer.toString() + e.getMessage()) .build(); } } /** * Diese Methode berechnet alle Metriken auf basis der existierenden Rechnungen * neu * * @throws QueryException * @throws InterruptedException * @throws PluginException * */ public void computeMetrics(StringBuffer messageBuffer) throws QueryException, InterruptedException, PluginException { long l = System.currentTimeMillis(); int batchSize = 500; int totalInvoices = 0; log("│   │   ├── recalculate metrics...", messageBuffer); String query = "$modelversion:rechnungsausgang-* AND type:workitem"; // Gesamtanzahl ermitteln int totalCount = documentService.count(query); log("│   │   ├── found " + totalCount + " open invoices", messageBuffer); // Berechne Anzahl der benötigten Pages int totalPages = (int) Math.ceil((double) totalCount / batchSize); // Verarbeite Page für Page for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) { totalInvoices = totalInvoices + computeInvoiceMetrics(query, batchSize, pageIndex); // List invoices = documentService.find(query, batchSize, // pageIndex); // for (ItemCollection invoice : invoices) { // try { // ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); // // Jetzt Rechnung addieren // metricDebitorService.addInvoice(metricData, invoice); // logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); // metricDebitorService.updateMetric(metricData, true); // totalInvoices++; // } catch (PluginException e) { // // invalid invoice - e.g. no dbtr. number // } // } // Fortschritt loggen log("│   │ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + " (" + totalInvoices + " of " + totalCount + " invoices)", messageBuffer); // Optional: Kurze Pause nach jedem 5. Batch Thread.sleep(100); } long duration = System.currentTimeMillis() - l; double invoicesPerSecond = totalInvoices / (duration / 1000.0); log("│   │   ├── Successfully processed " + totalInvoices + " invoices in " + duration + "ms (" + String.format("%.1f", invoicesPerSecond) + " invoices/sec)", messageBuffer); log("│   │   ├── Updated " + metricDebitorService.getMetricCount() + " metrics.", messageBuffer); } /** * Helper method runs in new transaction * * @param query * @param batchSize * @param pageIndex * @throws PluginException * @throws QueryException */ @TransactionAttribute(value = TransactionAttributeType.REQUIRES_NEW) public int computeInvoiceMetrics(String query, int batchSize, int pageIndex) throws PluginException, QueryException { int updates = 0; List invoices = documentService.find(query, batchSize, pageIndex); for (ItemCollection invoice : invoices) { try { ItemCollection metricData = metricDebitorService.getMetricByInvoice(invoice); // Jetzt Rechnung addieren metricDebitorService.addInvoice(metricData, invoice); logger.fine("│   │   │   ├── update metric " + InvoiceUtil.getBPId(invoice)); metricDebitorService.updateMetric(metricData, true); updates++; } catch (PluginException e) { // invalid invoice - e.g. no dbtr. number } } return updates; } private void log(String message, StringBuffer messageLog) { logger.info(message); messageLog.append(message + "\n"); } }