package es.uv.saic.web; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; import org.apache.commons.io.FilenameUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import es.uv.saic.service.DocumentService; import es.uv.saic.service.PlantillaService; import es.uv.saic.shared.domain.Document; import es.uv.saic.shared.dto.InstanciaTascaDTO; import es.uv.saic.shared.dto.OrganDTO; import es.uv.saic.shared.dto.PdfDTO; import es.uv.saic.shared.dto.ProcesDTO; import es.uv.saic.shared.dto.TascaDTO; import es.uv.saic.shared.dto.TascaInformeTransferDTO; import es.uv.saic.shared.feign.IndicadorClient; import es.uv.saic.shared.feign.OrganClient; import es.uv.saic.shared.feign.ProceduresClient; import es.uv.saic.shared.feign.TascaClient; import fr.opensagres.xdocreport.core.XDocReportException; import fr.opensagres.xdocreport.core.io.internal.ByteArrayOutputStream; import fr.opensagres.xdocreport.document.IXDocReport; import fr.opensagres.xdocreport.document.images.FileImageProvider; import fr.opensagres.xdocreport.document.images.IImageProvider; import fr.opensagres.xdocreport.document.registry.XDocReportRegistry; import fr.opensagres.xdocreport.template.IContext; import fr.opensagres.xdocreport.template.TemplateEngineKind; import fr.opensagres.xdocreport.template.formatter.FieldsMetadata; import jakarta.servlet.http.HttpServletResponse; @RestController public class DownloadController { @Autowired private PlantillaService pls; @Autowired private DocumentService ds; @Value("${saic.data.filePath}") private String filePath; @Value("${saic.data.docsPath}") private String docsPath; @Value("${saic.data.templates.fileNotFound}") private String fileNotFound; @Value("${saic.data.templates.filePath}") private String templatePath; @Value("${saic.data.templates.logoPath}") private String logoPath; @Autowired private TascaClient tc; @Autowired private OrganClient oc; @Autowired private IndicadorClient ic; @Autowired private ProceduresClient pc; /* * Download a file associated with a task instance * @param model * @param idInstanciaTasca The ID of the task instance * @param response HttpServletResponse * @return A FileSystemResource * representing the file to download */ @GetMapping(value="/download/{fileName}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity download(@PathVariable("fileName") BigInteger idInstanciaTasca, HttpServletResponse response) throws FileNotFoundException { try { InstanciaTascaDTO i = tc.findInstanciaTascaById(idInstanciaTasca); FileSystemResource file = null; if(i.getTasca().getIdTipus() == 22){ file = new FileSystemResource(i.getEvidencia()); String extension = "."+FilenameUtils.getExtension(i.getEvidencia()); String name = "-"+i.getTasca().getNomEvidenciaCas().replace(" ", "_"); response.setHeader("Content-Disposition", "attachment; filename="+i.getTasca().getCodiEvidencia()+name+extension); } else{ file = new FileSystemResource(this.filePath+i.getEvidencia()); response.setHeader("Content-Disposition", "attachment; filename="+i.getEvidencia()); } if (!file.exists()) { return ResponseEntity.ok(new FileSystemResource(this.fileNotFound).getInputStream().readAllBytes()); } return ResponseEntity.ok(file.getInputStream().readAllBytes()); } catch (IOException e) { e.printStackTrace(); } return null; } // PARA BORRAR /* * Download a document by its ID * @param model * @param idDocument The ID of the document to download * @param response HttpServletResponse * @return A FileSystemResource representing the document to download */ @GetMapping(value="/download/document/{id}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity downloadDocument(@PathVariable("id") Integer idDocument, HttpServletResponse response) { Document document = ds.findById(idDocument); FileSystemResource file = new FileSystemResource(document.getRuta()); if(!file.exists()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } response.setHeader("Content-Disposition", "attachment; filename="+file.getFilename()); try { return ResponseEntity.ok(file.getInputStream().readAllBytes()); } catch (IOException e) { e.printStackTrace(); } return null; } /* * Download the latest report for a given process and degree * @param model * @param idTitulacio The ID of the degree * @param nomProces The name of the process * @param response HttpServletResponse * @return A FileSystemResource representing the report to download */ @GetMapping(value="/download/report/{t}/{p}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity downloadReport(@PathVariable("t") Integer idTitulacio, @PathVariable("p") String nomProces, HttpServletResponse response) throws IOException, XDocReportException { OrganDTO titulacio = oc.findByID("T", idTitulacio); ProcesDTO procesDTO = new ProcesDTO(nomProces, titulacio.getLugar(), titulacio.getLugar2(), titulacio.getTambit()); TascaInformeTransferDTO it = tc.getLastByProcName(procesDTO); if(it != null) { if((new File(this.filePath+it.getEvidencia())).exists()) { response.setHeader("Content-Disposition", "attachment; filename="+Integer.toString(idTitulacio)+"_"+nomProces+".pdf"); return ResponseEntity.ok(new FileSystemResource(this.filePath+it.getEvidencia()).getInputStream().readAllBytes()); } } return ResponseEntity.ok(new FileSystemResource(this.filePath+it.getEvidencia()).getInputStream().readAllBytes()); } /* * Download a populated template for a given task instance * @param model * @param idTascai The ID of the task instance * @param response HttpServletResponse * @return A byte array representing the populated template to download */ @GetMapping(value="/download/template/{id}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity downloadTemplate(@PathVariable("id") BigInteger idTascai, HttpServletResponse response) throws IOException, XDocReportException { XDocReportRegistry.getRegistry().clear(); String reportId = "none"; InstanciaTascaDTO it = tc.findInstanciaTascaById(idTascai); TascaDTO tasca = it.getTasca(); /* Check if specific template exists */ Integer idTitulacio = it.getInstancia().getTitulacio(); String templatePath = this.templatePath+tasca.getCodiEvidencia().replace(".", "_")+".docx"; if(it.getInstancia().getTambit().equals("G") | idTitulacio == 1) { File f = new File(this.templatePath+"/T1/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T1/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"; } } else if(it.getInstancia().getTambit().equals("M") | idTitulacio == 2) { File f = new File(this.templatePath+"/T2/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T2/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"; } } File f = new File(templatePath); if(!f.exists()) { return ResponseEntity.ok(new FileSystemResource(this.fileNotFound).getInputStream().readAllBytes()); } InputStream in = new FileInputStream(f); IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in, reportId, TemplateEngineKind.Velocity); FieldsMetadata metadata = new FieldsMetadata(); metadata.addFieldAsImage("logo"); report.setFieldsMetadata(metadata); IContext context = report.createContext(); IImageProvider img; if(new File(this.logoPath+"C"+Integer.toString(it.getInstancia().getCentre())+".png").exists()) { img = new FileImageProvider(new File(this.logoPath+"C"+Integer.toString(it.getInstancia().getCentre())+".png")); } else { img = new FileImageProvider(new File(this.logoPath+"C0.png")); } context.put("logo", img); context.put("centre", it.getInstancia().getNomval()); context.put("titulacio", it.getInstancia().getNomcas()); context.put("curs", Integer.toString(it.getInstancia().getCursAvaluat()-1)+" - "+Integer.toString(it.getInstancia().getCursAvaluat())); context.put("curs_anterior", Integer.toString(it.getInstancia().getCursAvaluat()-2)+" - "+Integer.toString(it.getInstancia().getCursAvaluat()-1)); Integer idCentre = it.getInstancia().getCentre(); if(tasca.getIdTipus() == 14) { // Iterable template task List titulacions = new ArrayList(); Integer ambit = idTitulacio/(int)1000; titulacions = oc.getTitulacionsByTypeCentre(it.getInstancia().getLugar(), ambit); List> data = new ArrayList>(); for(OrganDTO x : titulacions) { HashMap t = ic.getTemplateDataArray(x.getRuct(), idCentre, it.getInstancia().getCursAvaluat(), x.getTambit()); t.put("titulacio", x.getNomCas()); data.add(t); } context.put("data", data); addToContext(ic.getTemplateData(idTitulacio, idCentre, it.getInstancia().getCursAvaluat()), context); } else { // NO iterable template task addToContext(ic.getTemplateData(idTitulacio, idCentre, it.getInstancia().getCursAvaluat()), context); } ByteArrayOutputStream out = new ByteArrayOutputStream(); report.process(context, out); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+tasca.getCodiEvidencia()+".docx\""); return ResponseEntity.ok(out.toByteArray()); } /* * Test endpoint to generate a populated template for a given task (id of task) * @param model * @param idTitulacio The ID of the degree * @param idCentre The ID of the center * @param idTascap The ID of the task * @param idProces The ID of the process * @param response HttpServletResponse * @return A byte array representing the populated template */ @GetMapping(value="/download/test/template/{ruct}/{centre}/{idProces}/{idTascap}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity testTemplate(@PathVariable("ruct") String ruct, @PathVariable("centre") Integer idCentre, @PathVariable("idTascap") Integer idTascap, @PathVariable("idProces") Integer idProces, HttpServletResponse response) throws IOException, XDocReportException { XDocReportRegistry.getRegistry().clear(); String reportId = "none"; TascaDTO tasca = tc.getByProcesTascap(idProces, idTascap); ProcesDTO proces = pc.findById(idProces); OrganDTO titulacio = oc.findByRuct(Integer.parseInt(ruct)); OrganDTO centre = oc.findByID("C", idCentre); /* Check if specific template exists */ String templatePath = this.templatePath+tasca.getCodiEvidencia().replace(".", "_")+".docx"; if(titulacio.getTambit().equals("G")) { File f = new File(this.templatePath+"/T1/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T1/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"; } } else if(titulacio.getTambit().equals("M")) { File f = new File(this.templatePath+"/T2/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T2/"+tasca.getCodiEvidencia().replace(".", "_")+".docx"; } } InputStream in = new FileInputStream(new File(templatePath)); IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in, reportId, TemplateEngineKind.Velocity); FieldsMetadata metadata = new FieldsMetadata(); metadata.addFieldAsImage("logo"); report.setFieldsMetadata(metadata); IContext context = report.createContext(); IImageProvider img; if(new File(this.logoPath+"C"+Integer.toString(idCentre)+".png").exists()) { img = new FileImageProvider(new File(this.logoPath+"C"+Integer.toString(idCentre)+".png")); } else { img = new FileImageProvider(new File(this.logoPath+"C0.png")); } context.put("logo", img); context.put("centre", centre.getNomVal()); context.put("titulacio", titulacio.getNomVal()); context.put("curs", Integer.toString(proces.getCursAvaluat()-1)+" - "+Integer.toString(proces.getCursAvaluat())); context.put("curs_anterior", Integer.toString(proces.getCursAvaluat()-2)+" - "+Integer.toString(proces.getCursAvaluat()-1)); if(tasca.getIdTipus() == 14) { // Iterable template task List titulacions = new ArrayList(); titulacions = oc.getTitulacionsByTypeCentre(centre.getRuct(), centre.getLugar()); List> data = new ArrayList>(); for(OrganDTO x : titulacions) { HashMap t = ic.getTemplateDataArray(Integer.parseInt(ruct), idCentre, proces.getCursAvaluat(), x.getTambit()); t.put("titulacio", x.getNomCas()); data.add(t); } context.put("data", data); addToContext(ic.getTemplateData(titulacio.getRuct(), idCentre, proces.getCursAvaluat()), context); } else { // NO iterable template task addToContext(ic.getTemplateData(titulacio.getRuct(), idCentre, proces.getCursAvaluat()), context); } ByteArrayOutputStream out = new ByteArrayOutputStream(); report.process(context, out); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+tasca.getCodiEvidencia()+".docx\""); return ResponseEntity.ok(out.toByteArray()); } /* * Test endpoint to generate a populated template for a given degree and evidence (Type of task) * @param model * @param idTitulacio The ID of the degree * @param idCentre The ID of the center * @param evidencia The name of the evidence * @param curs The academic year * @param tipusTasca The type of task * @param response HttpServletResponse * @return A byte array representing the populated template */ @GetMapping(value="/download/test/template2/{ruct}/{centre}/{evidencia}/{curs}/{tipusTasca}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity testTemplate(Model model, @PathVariable("ruct") Integer ruct, @PathVariable("centre") Integer idCentre, @PathVariable("evidencia") String evidencia, @PathVariable("curs") Integer curs, @PathVariable("tipusTasca") Integer tipusTasca, HttpServletResponse response) throws IOException, XDocReportException { XDocReportRegistry.getRegistry().clear(); String reportId = "none"; OrganDTO titulacio = oc.findByRuct(ruct); OrganDTO centre = oc.findByID("C", idCentre); /* Check if specific template exists */ String templatePath = this.templatePath+evidencia.replace(".", "_").replace(" (G)", "").replace(" (M)", "")+".docx"; if(titulacio.getLugar() < 2000 | titulacio.getLugar() == 1) { File f = new File(this.templatePath+"/T1/"+evidencia.replace(".", "_").replace(" (G)", "").replace(" (M)", "")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T1/"+evidencia.replace(".", "_").replace(" (G)", "").replace(" (M)", "")+".docx"; }; } else if((titulacio.getLugar() >= 2000 & titulacio.getLugar() < 3000) | titulacio.getLugar() == 2) { File f = new File(this.templatePath+"/T2/"+evidencia.replace(".", "_").replace(" (G)", "").replace(" (M)", "")+".docx"); if(f.exists() && !f.isDirectory()) { templatePath = this.templatePath+"/T2/"+evidencia.replace(".", "_").replace(" (G)", "").replace(" (M)", "")+".docx"; } } InputStream in = new FileInputStream(new File(templatePath)); IXDocReport report = XDocReportRegistry.getRegistry().loadReport(in, reportId, TemplateEngineKind.Velocity); FieldsMetadata metadata = new FieldsMetadata(); metadata.addFieldAsImage("logo"); report.setFieldsMetadata(metadata); IContext context = report.createContext(); IImageProvider img; if(new File(this.logoPath+"C"+Integer.toString(idCentre)+".png").exists()) { img = new FileImageProvider(new File(this.logoPath+"C"+Integer.toString(idCentre)+".png")); } else { img = new FileImageProvider(new File(this.logoPath+"C0.png")); } context.put("logo", img); context.put("centre", centre.getNomVal()); context.put("titulacio", titulacio.getNomVal()); context.put("curs", Integer.toString(curs-1)+" - "+Integer.toString(curs)); context.put("curs_anterior", Integer.toString(curs-2)+" - "+Integer.toString(curs-1)); if(tipusTasca == 14) { // Iterable template task List titulacions = new ArrayList(); titulacions = oc.getTitulacionsByTypeCentre(centre.getRuct(), centre.getLugar()); List> data = new ArrayList>(); for(OrganDTO x : titulacions) { HashMap t = ic.getTemplateDataArray(x.getRuct(), idCentre, curs, x.getTambit()); t.put("titulacio", x.getNomCas()); data.add(t); } context.put("data", data); addToContext(ic.getTemplateData(ruct, idCentre, curs), context); } else { // NO iterable template task addToContext(ic.getTemplateData(ruct, idCentre, curs), context); } ByteArrayOutputStream out = new ByteArrayOutputStream(); report.process(context, out); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+evidencia+".docx\""); response.setHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); return ResponseEntity.ok(out.toByteArray()); } /* * Generate a PDF from the content of a task instance (unused) * @param model * @param idTascai The ID of the task instance * @param response HttpServletResponse * @return A byte array representing the generated PDF */ @GetMapping(value="/download/pdf/{idTascai}") @ResponseBody public byte[] downloadTemplatePdf(@PathVariable("idTascai") BigInteger idTascai, HttpServletResponse response) throws IOException, InterruptedException { InstanciaTascaDTO it = tc.findInstanciaTascaById(idTascai); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+it.getIdInstanciaTasca()+".pdf\""); response.setHeader(HttpHeaders.CONTENT_TYPE, "application/pdf"); return pls.toPDF(it.getText(), Optional.of(idTascai)); } /* * Generate a PDF preview from provided content * @param model * @param content The content to convert to PDF * @param idtascai Optional ID of the task instance for context * @param response HttpServletResponse */ @PostMapping(value="/download/pdf/preview") @ResponseBody public byte[] downloadTemplatePdf(HttpServletResponse response, @RequestBody PdfDTO pdf) throws IOException, InterruptedException { response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"borrador.pdf\""); response.setHeader(HttpHeaders.CONTENT_TYPE, "application/pdf"); return pls.toPDF(pdf.getContent(), pdf.getIdtascai()); } private void addToContext(HashMap templateData, IContext context) { for (HashMap.Entry entry : templateData.entrySet()) { context.put(entry.getKey(), formatValue(entry.getValue())); } } @GetMapping(value="/download/{idInstancia}/{idTascap}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @ResponseBody public ResponseEntity downloadEvidenceByTascaP(@PathVariable("idInstancia") BigInteger idInstancia, @PathVariable("idTascap") Integer idTascap, HttpServletResponse response){ try { InstanciaTascaDTO i = tc.findInstanciaTascaByInstanciaAndTascaP(idInstancia, idTascap); FileSystemResource file = null; file = new FileSystemResource(this.filePath+i.getEvidencia()); response.setHeader("Content-Disposition", "attachment; filename="+i.getEvidencia()); if (!file.exists()) { return ResponseEntity.ok(new FileSystemResource(this.fileNotFound).getInputStream().readAllBytes()); } return ResponseEntity.ok(file.getInputStream().readAllBytes()); } catch (IOException e) { e.printStackTrace(); } return null; } /* * Format a value for template insertion * @param v The value to format * @return The formatted value */ private String formatValue(String v) { if(v == null) return ""; if(v.equals("NP")) return "NP"; if(v.isEmpty() | v.isBlank()) { return ""; } try{ return String.format("%.2f", Float.parseFloat(v)).replace(",", "."); } catch(NumberFormatException e){ } try{ return Integer.toString(Integer.parseInt(v)); } catch(NumberFormatException e){ } return v; } }