fix: resolve go vet error and dayStart logic in ComputeDailyTotals
- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf - Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking when dayStart > 0, correctly counting work from dayStart to first out event - Update README project structure to reflect new domain/handler/repo layout - Implement missing achievements: rpg_pioneer (on RPG enable) and salary_setter (on first rate set) - Add tryUnlockAchievement helper to Handler
This commit is contained in:
120
internal/handler/summary.go
Normal file
120
internal/handler/summary.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-telegram/bot/models"
|
||||
|
||||
"worktimeBot/internal/domain"
|
||||
)
|
||||
|
||||
func (h *Handler) handleSummary(ctx context.Context, msg *models.Message, user *domain.User) {
|
||||
loc := h.loadLocation(user.Timezone)
|
||||
now := time.Now().In(loc)
|
||||
|
||||
args := strings.Fields(msg.Text)
|
||||
if len(args) >= 1 && args[0][0] == '/' {
|
||||
args = args[1:]
|
||||
}
|
||||
|
||||
var calY, calM int
|
||||
if len(args) >= 1 {
|
||||
if _, err := fmt.Sscanf(args[0], "%d-%d", &calY, &calM); err != nil || calM < 1 || calM > 12 {
|
||||
h.sendText(ctx, msg.Chat.ID, "Usage: /summary [YYYY-MM] (e.g. /summary 2026-06)")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
calY, calM = now.Year(), int(now.Month())
|
||||
switch user.Calendar {
|
||||
case "jalali":
|
||||
calY, calM, _ = domain.GregorianToJalali(calY, calM, now.Day())
|
||||
case "hijri":
|
||||
calY, calM, _ = domain.GregorianToHijri(calY, calM, now.Day())
|
||||
}
|
||||
}
|
||||
|
||||
startStr, endStr := domain.MonthGregorianRange(user.Calendar, calY, calM)
|
||||
summary := h.buildMonthlySummary(user, calY, calM, startStr, endStr, loc)
|
||||
h.sendText(ctx, msg.Chat.ID, summary)
|
||||
}
|
||||
|
||||
func (h *Handler) buildMonthlySummary(user *domain.User, calY, calM int, startStr, endStr string, loc *time.Location) string {
|
||||
days, err := h.Repo.GetUserDaysInRange(user.ID, startStr, endStr)
|
||||
if err != nil {
|
||||
return "Error loading days"
|
||||
}
|
||||
|
||||
title := domain.FormatMonthTitle(user.Calendar, calY, calM)
|
||||
|
||||
totalWork := int64(0)
|
||||
totalBreak := int64(0)
|
||||
dayOffCount := 0
|
||||
workDayCount := 0
|
||||
dayDetails := []string{}
|
||||
|
||||
for _, day := range days {
|
||||
dayLabel := domain.FormatDateForCalendar(day.Date, user.Calendar)
|
||||
|
||||
if day.IsDayOff {
|
||||
dayOffCount++
|
||||
}
|
||||
|
||||
events, err := h.Repo.EventsForDayByDayID(day.ID)
|
||||
if err != nil || len(events) == 0 {
|
||||
if day.IsDayOff {
|
||||
dayDetails = append(dayDetails, fmt.Sprintf(" %s - Day Off", dayLabel))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
y, m, d := domain.ParseGregorianDateKey(day.Date)
|
||||
dayStart := time.Date(y, time.Month(m), d, 0, 0, 0, 0, loc).Unix()
|
||||
dayEnd := time.Date(y, time.Month(m), d, 23, 59, 59, 0, loc).Unix()
|
||||
totals := domain.ComputeDailyTotals(events, day.MinBreakThreshold, dayStart, dayEnd)
|
||||
if totals.TotalSeconds == 0 {
|
||||
if day.IsDayOff {
|
||||
dayDetails = append(dayDetails, fmt.Sprintf(" %s - Day Off", dayLabel))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
workDayCount++
|
||||
totalWork += totals.TotalSeconds
|
||||
totalBreak += totals.BreakSeconds
|
||||
label := fmt.Sprintf(" %s - %s worked", dayLabel, formatDuration(totals.TotalSeconds))
|
||||
if day.IsDayOff {
|
||||
label += " (Day Off)"
|
||||
}
|
||||
dayDetails = append(dayDetails, label)
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf("Summary for %s\n", title))
|
||||
b.WriteString(fmt.Sprintf("Work days: %d\n", workDayCount))
|
||||
if dayOffCount > 0 {
|
||||
b.WriteString(fmt.Sprintf("Days off: %d\n", dayOffCount))
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("Total: %s worked", formatDuration(totalWork)))
|
||||
if totalBreak > 0 {
|
||||
b.WriteString(fmt.Sprintf(", %s break", formatDuration(totalBreak)))
|
||||
}
|
||||
|
||||
st, _ := h.Repo.GetOrCreateSettings(user.ID)
|
||||
if st.HourlyRate > 0 {
|
||||
est := h.computeMonthlySalary(days, user.ID, st.HourlyRate, loc)
|
||||
b.WriteString(fmt.Sprintf("\nSalary: %s (%.1f hours, %d days)",
|
||||
est.FormatSalaryShort(st.Currency), est.WorkHours, est.WorkDays))
|
||||
} else {
|
||||
b.WriteString("\nSalary: No rate configured. Use /setrate <amount>")
|
||||
}
|
||||
|
||||
b.WriteString("\n\nDetails:\n")
|
||||
for _, d := range dayDetails {
|
||||
b.WriteString(d)
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user