excel download option

This commit is contained in:
Jörg Henke
2026-01-19 13:35:41 +01:00
parent 667837e24f
commit 2d97720895
7 changed files with 242 additions and 3 deletions

View File

@@ -17,7 +17,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
public class Main extends SpringBootServletInitializer {
private static final Integer requiredDbVersion = 20260114;
private static final Integer requiredDbVersion = 20260119;
public static final Logger LOGGER = LogManager.getLogger(Main.class);

View File

@@ -0,0 +1,38 @@
package de.jottyfan.timetrack.modules.projectmanagement;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import jakarta.annotation.security.RolesAllowed;
import jakarta.servlet.http.HttpServletResponse;
/**
*
* @author jotty
*
*/
@Controller
public class DownloadController {
@Autowired
private DownloadService service;
@Autowired
private HttpServletResponse response;
@RolesAllowed("timetrack_user")
@GetMapping("/projectmanagement/download")
public @ResponseBody void getDownloadExcel() throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String contentDisposition = String.format("attachment; filename=\"projectmanagement_%s.xlsx\"", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
service.generateExcel(response.getOutputStream());
}
}

View File

@@ -0,0 +1,39 @@
package de.jottyfan.timetrack.modules.projectmanagement;
import static de.jottyfan.timetrack.db.project.Tables.V_EXCEL_APP;
import static de.jottyfan.timetrack.db.project.Tables.V_EXCEL_WORKPACKAGE;
import static de.jottyfan.timetrack.db.project.Tables.V_EXCEL_WORKPACKAGE_APP;
import java.util.List;
import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import de.jottyfan.timetrack.db.project.tables.records.VExcelAppRecord;
import de.jottyfan.timetrack.db.project.tables.records.VExcelWorkpackageAppRecord;
import de.jottyfan.timetrack.db.project.tables.records.VExcelWorkpackageRecord;
/**
*
* @author jotty
*
*/
@Repository
public class DownloadRepository {
@Autowired
private DSLContext jooq;
public List<VExcelAppRecord> getAllApps() {
return jooq.selectFrom(V_EXCEL_APP).fetchInto(VExcelAppRecord.class);
}
public List<VExcelWorkpackageRecord> getAllWorkpackages() {
return jooq.selectFrom(V_EXCEL_WORKPACKAGE).fetchInto(VExcelWorkpackageRecord.class);
}
public List<VExcelWorkpackageAppRecord> getAllWorkpackageApps() {
return jooq.selectFrom(V_EXCEL_WORKPACKAGE_APP).fetchInto(VExcelWorkpackageAppRecord.class);
}
}

View File

@@ -0,0 +1,28 @@
package de.jottyfan.timetrack.modules.projectmanagement;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.servlet.ServletOutputStream;
/**
*
* @author jotty
*
*/
@Service
public class DownloadService {
@Autowired
private DownloadRepository repository;
public void generateExcel(ServletOutputStream outputStream) throws IOException {
ExcelWriter writer = new ExcelWriter();
writer.addSheet("workpackages", repository.getAllWorkpackages());
writer.addSheet("workpackage apps", repository.getAllWorkpackageApps());
writer.addSheet("apps", repository.getAllApps());
writer.bundle(outputStream);
}
}

View File

@@ -0,0 +1,131 @@
package de.jottyfan.timetrack.modules.projectmanagement;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dhatim.fastexcel.Workbook;
import org.dhatim.fastexcel.Worksheet;
import org.jooq.impl.TableRecordImpl;
import de.jottyfan.timetrack.db.project.tables.records.VExcelAppRecord;
import de.jottyfan.timetrack.db.project.tables.records.VExcelWorkpackageAppRecord;
import de.jottyfan.timetrack.db.project.tables.records.VExcelWorkpackageRecord;
/**
*
* @author jotty
*
*/
public class ExcelWriter {
private final Map<String, List<? extends TableRecordImpl<?>>> sheetMap;
public ExcelWriter() {
this.sheetMap = new HashMap<>();
}
public void addSheet(String name, List<? extends TableRecordImpl<?>> data) {
sheetMap.put(name, data);
}
public void bundle(OutputStream stream) throws IOException {
try (Workbook workbook = new Workbook(stream, "timetrack", "1.0")) {
sheetMap.forEach((key, sheetObjects) -> {
Worksheet worksheet = workbook.newWorksheet(key);
sheetObjects.forEach(so -> {
handleHeadLines(worksheet, so);
});
if (sheetObjects.size() > 0) {
Integer lineCounter = 1;
for (TableRecordImpl<?> sheetObject : sheetObjects) {
if (sheetObject instanceof VExcelWorkpackageRecord rec) {
lineCounter = fillFields(worksheet, rec, lineCounter);
} else if (sheetObject instanceof VExcelWorkpackageAppRecord rec) {
lineCounter = fillFields(worksheet, rec, lineCounter);
} else if (sheetObject instanceof VExcelAppRecord rec) {
lineCounter = fillFields(worksheet, rec, lineCounter);
} else {
throw new RuntimeException("unsupported TabelRecordImpl " + sheetObject.getClass().getSimpleName());
}
}
}
});
workbook.close();
}
stream.flush();
}
private void handleHeadLines(Worksheet worksheet, TableRecordImpl<?> tri) {
if (tri instanceof VExcelWorkpackageRecord) {
worksheet.value(0, 0, "project_id");
worksheet.value(0, 1, "project_name");
worksheet.value(0, 2, "project_description");
worksheet.value(0, 3, "funder_name");
worksheet.value(0, 4, "funder_description");
worksheet.value(0, 5, "workpackage_id");
worksheet.value(0, 6, "workpackage_name");
worksheet.value(0, 7, "workpackage_description");
worksheet.value(0, 8, "contract_url");
worksheet.value(0, 9, "milestone_url");
worksheet.value(0, 10, "planned_duedate");
} else if (tri instanceof VExcelWorkpackageAppRecord) {
worksheet.value(0, 0, "workpackage_app_id");
worksheet.value(0, 1, "workpackage_name");
worksheet.value(0, 2, "app_name");
worksheet.value(0, 3, "workpackage_id");
worksheet.value(0, 4, "app_id");
} else if (tri instanceof VExcelAppRecord) {
worksheet.value(0, 0, "app_id");
worksheet.value(0, 1, "replaced_by_app_with_id");
worksheet.value(0, 2, "name");
worksheet.value(0, 3, "description");
worksheet.value(0, 4, "basic_functionality");
worksheet.value(0, 5, "bundle_name");
worksheet.value(0, 6, "bundle_description");
worksheet.value(0, 7, "repository_url");
worksheet.value(0, 8, "orphan");
} else {
throw new RuntimeException("unsupported TabelRecordImpl " + tri.getClass().getSimpleName());
}
}
private Integer fillFields(Worksheet worksheet, VExcelWorkpackageRecord record, Integer lineCounter) {
worksheet.value(lineCounter, 0, record.getProjectId());
worksheet.value(lineCounter, 1, record.getProjectName());
worksheet.value(lineCounter, 2, record.getProjectDescription());
worksheet.value(lineCounter, 3, record.getFunderName());
worksheet.value(lineCounter, 4, record.getFunderDescription());
worksheet.value(lineCounter, 5, record.getWorkpackageId());
worksheet.value(lineCounter, 6, record.getWorkpackageName());
worksheet.value(lineCounter, 7, record.getWorkpackageDescription());
worksheet.value(lineCounter, 8, record.getContractUrl());
worksheet.value(lineCounter, 9, record.getMilestoneUrl());
worksheet.value(lineCounter, 10, record.getPlannedDuedate());
return lineCounter + 1;
}
private Integer fillFields(Worksheet worksheet, VExcelWorkpackageAppRecord record, Integer lineCounter) {
worksheet.value(lineCounter, 0, record.getWorkpackageAppId());
worksheet.value(lineCounter, 1, record.getWorkpackageName());
worksheet.value(lineCounter, 2, record.getAppName());
worksheet.value(lineCounter, 3, record.getWorkpackageId());
worksheet.value(lineCounter, 4, record.getAppId());
return lineCounter + 1;
}
private Integer fillFields(Worksheet worksheet, VExcelAppRecord record, Integer lineCounter) {
worksheet.value(lineCounter, 0, record.getAppId());
worksheet.value(lineCounter, 1, record.getReplacedByAppWithId());
worksheet.value(lineCounter, 2, record.getName());
worksheet.value(lineCounter, 3, record.getDescription());
worksheet.value(lineCounter, 4, record.getBasicFunctionality());
worksheet.value(lineCounter, 5, record.getBundleName());
worksheet.value(lineCounter, 6, record.getBundleDescription());
worksheet.value(lineCounter, 7, record.getRepositoryUrl());
worksheet.value(lineCounter, 8, record.getOrphan());
return lineCounter + 1;
}
}

View File

@@ -7,7 +7,8 @@
<font layout:fragment="title">Projekte</font>
<ul layout:fragment="menu">
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
<a class="nav-link btn btn-success btn-white-text" th:href="@{/projectmanagement/project/add}">Neues Projekt anlegen</a>
<a class="btn btn-outline-success" th:href="@{/projectmanagement/project/add}">Neues Projekt anlegen</a>
<a class="btn btn-outline-secondary" th:href="@{/projectmanagement/download}">Export</a>
</li>
</ul>
<main layout:fragment="content">