excel download option
This commit is contained in:
@@ -24,7 +24,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'de.jottyfan:timetrackjooq:20260114'
|
implementation 'de.jottyfan:timetrackjooq:20260119'
|
||||||
|
|
||||||
implementation 'org.webjars:bootstrap:5.3.8'
|
implementation 'org.webjars:bootstrap:5.3.8'
|
||||||
implementation 'org.webjars:font-awesome:7.1.0'
|
implementation 'org.webjars:font-awesome:7.1.0'
|
||||||
@@ -36,6 +36,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.google.code.gson:gson';
|
implementation 'com.google.code.gson:gson';
|
||||||
|
|
||||||
|
implementation 'org.dhatim:fastexcel:0.19.1'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
|
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
public class Main extends SpringBootServletInitializer {
|
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);
|
public static final Logger LOGGER = LogManager.getLogger(Main.class);
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,8 @@
|
|||||||
<font layout:fragment="title">Projekte</font>
|
<font layout:fragment="title">Projekte</font>
|
||||||
<ul layout:fragment="menu">
|
<ul layout:fragment="menu">
|
||||||
<li class="nav-item" sec:authorize="hasRole('timetrack_user')">
|
<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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<main layout:fragment="content">
|
<main layout:fragment="content">
|
||||||
|
|||||||
Reference in New Issue
Block a user