added pdf generation with typst, working on pdf input form
This commit is contained in:
@@ -4,17 +4,23 @@ import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"arbeitszeitmessung/models"
|
||||
"arbeitszeitmessung/templates"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Dadido3/go-typst"
|
||||
)
|
||||
|
||||
type typstMetadata struct {
|
||||
ISOWeek string `json:"iso-week"`
|
||||
EmployeeName string `json:"employee-name"`
|
||||
WorkTime string `json:"worktime"`
|
||||
Overtime string `json:"overtime"`
|
||||
OvertimeTotal string `json:"overtime-total"`
|
||||
TimeRange string `json:"time-range"`
|
||||
EmployeeName string `json:"employee-name"`
|
||||
WorkTime string `json:"worktime"`
|
||||
Overtime string `json:"overtime"`
|
||||
OvertimeTotal string `json:"overtime-total"`
|
||||
CurrentTimestamp string `json:"current-timestamp"`
|
||||
}
|
||||
|
||||
type typstDayPart struct {
|
||||
@@ -30,38 +36,100 @@ type typstDay struct {
|
||||
Worktime string `json:"worktime"`
|
||||
Pausetime string `json:"pausetime"`
|
||||
Overtime string `json:"overtime"`
|
||||
IsFriday bool `json:"is-weekend"`
|
||||
}
|
||||
|
||||
func ConvertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, error) {
|
||||
func convertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, error) {
|
||||
var typstDays []typstDay
|
||||
for _, day := range days {
|
||||
var typstDay typstDay
|
||||
var typstDayParts []typstDayPart
|
||||
var thisTypstDay typstDay
|
||||
work, pause, overtime := day.GetAllWorkTimesVirtual(u)
|
||||
typstDay.Date = day.Date().Format("01.02.2006")
|
||||
typstDay.Worktime = helper.FormatDuration(work)
|
||||
typstDay.Pausetime = helper.FormatDuration(pause)
|
||||
typstDay.Overtime = helper.FormatDuration(overtime)
|
||||
if day.IsWorkDay() {
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||
var typstDayPart typstDayPart
|
||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
typstDayPart.BookingTo = workDay.Bookings[i+1].Timestamp.Format("15:04")
|
||||
typstDayPart.WorkType = workDay.Bookings[i].BookingType.Name
|
||||
typstDayPart.IsWorkDay = true
|
||||
typstDayParts = append(typstDayParts, typstDayPart)
|
||||
}
|
||||
} else {
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: absentDay.AbwesenheitTyp.Name})
|
||||
}
|
||||
typstDay.DayParts = typstDayParts
|
||||
typstDays = append(typstDays, typstDay)
|
||||
thisTypstDay.Date = day.Date().Format("02.01.2006")
|
||||
thisTypstDay.Worktime = helper.FormatDurationFill(work, true)
|
||||
thisTypstDay.Pausetime = helper.FormatDurationFill(pause, true)
|
||||
thisTypstDay.Overtime = helper.FormatDurationFill(overtime, true)
|
||||
thisTypstDay.IsFriday = day.Date().Weekday() == time.Friday
|
||||
|
||||
thisTypstDay.DayParts = convertDayToTypstDayParts(day, u)
|
||||
typstDays = append(typstDays, thisTypstDay)
|
||||
}
|
||||
return typstDays, nil
|
||||
}
|
||||
|
||||
func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDayPart {
|
||||
var typstDayParts []typstDayPart
|
||||
if day.IsWorkDay() {
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||
var typstDayPart typstDayPart
|
||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
typstDayPart.BookingTo = workDay.Bookings[i+1].Timestamp.Format("15:04")
|
||||
typstDayPart.WorkType = workDay.Bookings[i].BookingType.Name
|
||||
typstDayPart.IsWorkDay = true
|
||||
typstDayParts = append(typstDayParts, typstDayPart)
|
||||
}
|
||||
if day.IsKurzArbeit() {
|
||||
tsFrom, tsTo := workDay.GenerateKurzArbeitBookings(user)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{
|
||||
BookingFrom: tsFrom.Format("15:04"),
|
||||
BookingTo: tsTo.Format("15:04"),
|
||||
WorkType: "Kurzarbeit",
|
||||
IsWorkDay: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
absentDay, _ := day.(*models.Absence)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: absentDay.AbwesenheitTyp.Name})
|
||||
}
|
||||
return typstDayParts
|
||||
}
|
||||
|
||||
func renderPDF(days []typstDay, metadata typstMetadata) (bytes.Buffer, error) {
|
||||
var markup bytes.Buffer
|
||||
var output bytes.Buffer
|
||||
|
||||
if err := typst.InjectValues(&markup, map[string]any{"meta": metadata, "days": days}); err != nil {
|
||||
return output, err
|
||||
}
|
||||
|
||||
// Import the template and invoke the template function with the custom data.
|
||||
// Show is used to replace the current document with whatever content the template function in `template.typ` returns.
|
||||
markup.WriteString(`
|
||||
#import "template.typ": abrechnung
|
||||
#show: doc => abrechnung(meta, days)
|
||||
`)
|
||||
|
||||
// Compile the prepared markup with Typst and write the result it into `output.pdf`.
|
||||
// f, err := os.Create("output.pdf")
|
||||
// if err != nil {
|
||||
// log.Panicf("Failed to create output file: %v.", err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
//
|
||||
|
||||
typstCLI := typst.CLI{}
|
||||
if err := typstCLI.Compile(&markup, &output, nil); err != nil {
|
||||
return output, err
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func PDFFormHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
|
||||
user, err := models.GetUserFromSession(Session, r.Context())
|
||||
if err != nil {
|
||||
slog.Warn("Error getting user!", slog.Any("Error", err))
|
||||
// TODO add error handling
|
||||
}
|
||||
|
||||
teamMembers, err := user.GetTeamMembers()
|
||||
if err != nil {
|
||||
slog.Warn("Error getting team members!", slog.Any("Error", err))
|
||||
}
|
||||
templates.PDFForm(teamMembers).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func PDFHandler(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
startDate, err := parseTimestamp(r, "start_date", time.Now().Format("2006-01-02"))
|
||||
@@ -89,6 +157,25 @@ func PDFHandler(w http.ResponseWriter, r *http.Request) {
|
||||
aggregatedWorkTime += day.TimeWorkVirtual(user)
|
||||
}
|
||||
|
||||
// log.Printf("Using Dates: %s - %s\n", startDate.String(), endDate.String())
|
||||
templates.PDFReportEmploye(user, aggregatedOvertime, aggregatedWorkTime, weeks, startDate, endDate).Render(r.Context(), w)
|
||||
typstDays, err := convertDaysToTypst(weeks, user)
|
||||
if err != nil {
|
||||
log.Panicf("Failed to convert days!")
|
||||
}
|
||||
metadata := typstMetadata{
|
||||
EmployeeName: fmt.Sprintf("%s %s", user.Vorname, user.Name),
|
||||
TimeRange: fmt.Sprintf("%s - %s", startDate.Format("02.01.2006"), endDate.Format("02.01.2006")),
|
||||
Overtime: helper.FormatDurationFill(aggregatedOvertime, true),
|
||||
WorkTime: helper.FormatDurationFill(aggregatedWorkTime, true),
|
||||
OvertimeTotal: "",
|
||||
CurrentTimestamp: time.Now().Format("02.01.2006 - 15:04 Uhr"),
|
||||
}
|
||||
|
||||
output, err := renderPDF(typstDays, metadata)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
}
|
||||
|
||||
w.Header().Set("Content-type", "application/pdf")
|
||||
output.WriteTo(w)
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user