219 lines
6.7 KiB
Go
219 lines
6.7 KiB
Go
package batch
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"queryorchestration/internal/database/repository"
|
|
"queryorchestration/internal/serviceconfig"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
// BatchUploadSummary represents a summary of a batch upload
|
|
type BatchUploadSummary struct {
|
|
ID uuid.UUID `json:"batch_id"`
|
|
ClientID string `json:"client_id"`
|
|
OriginalFilename string `json:"original_filename"`
|
|
Status string `json:"status"`
|
|
TotalDocuments int32 `json:"total_documents"`
|
|
ProcessedDocuments int32 `json:"processed_documents"`
|
|
FailedDocuments int32 `json:"failed_documents"`
|
|
InvalidTypeDocuments int32 `json:"invalid_type_documents"`
|
|
ProgressPercent int32 `json:"progress_percent"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
|
}
|
|
|
|
// BatchUploadDetails includes full details with failed filenames
|
|
type BatchUploadDetails struct {
|
|
BatchUploadSummary
|
|
FailedFilenames []string `json:"failed_filenames"`
|
|
}
|
|
|
|
// Service handles batch upload operations
|
|
type Service struct {
|
|
cfg serviceconfig.ConfigProvider
|
|
}
|
|
|
|
// New creates a new batch service
|
|
func New(cfg serviceconfig.ConfigProvider) *Service {
|
|
return &Service{
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
// Create creates a new batch upload record
|
|
func (s *Service) Create(ctx context.Context, clientID string, filename string, totalDocs int32) (uuid.UUID, error) {
|
|
return s.cfg.GetDBQueries().CreateBatchUpload(ctx, &repository.CreateBatchUploadParams{
|
|
ClientID: clientID,
|
|
OriginalFilename: filename,
|
|
TotalDocuments: totalDocs,
|
|
})
|
|
}
|
|
|
|
// Get retrieves batch upload details
|
|
func (s *Service) Get(ctx context.Context, clientID string, batchID uuid.UUID) (*BatchUploadDetails, error) {
|
|
batch, err := s.cfg.GetDBQueries().GetBatchUpload(ctx, &repository.GetBatchUploadParams{
|
|
ID: batchID,
|
|
ClientID: clientID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s.convertToDetails(batch), nil
|
|
}
|
|
|
|
// List retrieves all batch uploads for a client with pagination
|
|
func (s *Service) List(ctx context.Context, clientID string, limit int32, offset int32) ([]*BatchUploadSummary, error) {
|
|
if limit <= 0 {
|
|
limit = 20 // default limit
|
|
}
|
|
if limit > 100 {
|
|
limit = 100 // max limit
|
|
}
|
|
|
|
batches, err := s.cfg.GetDBQueries().ListBatchUploads(ctx, &repository.ListBatchUploadsParams{
|
|
ClientID: clientID,
|
|
Limit: int64(limit),
|
|
Offset: int64(offset),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
summaries := make([]*BatchUploadSummary, len(batches))
|
|
for i, batch := range batches {
|
|
summaries[i] = s.convertToSummary(batch)
|
|
}
|
|
|
|
return summaries, nil
|
|
}
|
|
|
|
// Cancel cancels a batch upload in progress
|
|
func (s *Service) Cancel(ctx context.Context, clientID string, batchID uuid.UUID) error {
|
|
return s.cfg.GetDBQueries().CancelBatchUpload(ctx, &repository.CancelBatchUploadParams{
|
|
ID: batchID,
|
|
ClientID: clientID,
|
|
})
|
|
}
|
|
|
|
// UpdateProgress updates batch processing progress
|
|
func (s *Service) UpdateProgress(ctx context.Context, clientID string, batchID uuid.UUID, processed, failed, invalidType int32) error {
|
|
// Calculate progress percentage
|
|
batch, err := s.cfg.GetDBQueries().GetBatchUpload(ctx, &repository.GetBatchUploadParams{
|
|
ID: batchID,
|
|
ClientID: clientID,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get batch for progress update: %w", err)
|
|
}
|
|
|
|
total := batch.TotalDocuments
|
|
if total == 0 {
|
|
return fmt.Errorf("batch has zero total documents")
|
|
}
|
|
|
|
progressPercent := int32(float64(processed+failed+invalidType) / float64(total) * 100)
|
|
|
|
return s.cfg.GetDBQueries().UpdateBatchProgress(ctx, &repository.UpdateBatchProgressParams{
|
|
ID: batchID,
|
|
ProcessedDocuments: processed,
|
|
FailedDocuments: failed,
|
|
InvalidTypeDocuments: invalidType,
|
|
ProgressPercent: progressPercent,
|
|
})
|
|
}
|
|
|
|
// MarkCompleted marks a batch as completed
|
|
func (s *Service) MarkCompleted(ctx context.Context, batchID uuid.UUID) error {
|
|
return s.cfg.GetDBQueries().UpdateBatchStatus(ctx, &repository.UpdateBatchStatusParams{
|
|
ID: batchID,
|
|
Column2: repository.BatchStatusCompleted,
|
|
})
|
|
}
|
|
|
|
// MarkFailed marks a batch as failed
|
|
func (s *Service) MarkFailed(ctx context.Context, batchID uuid.UUID) error {
|
|
return s.cfg.GetDBQueries().UpdateBatchStatus(ctx, &repository.UpdateBatchStatusParams{
|
|
ID: batchID,
|
|
Column2: repository.BatchStatusFailed,
|
|
})
|
|
}
|
|
|
|
// AddFailedFilename adds a failed filename to the batch
|
|
func (s *Service) AddFailedFilename(ctx context.Context, batchID uuid.UUID, filename string) error {
|
|
return s.cfg.GetDBQueries().AddFailedFilename(ctx, &repository.AddFailedFilenameParams{
|
|
ID: batchID,
|
|
Column2: filename,
|
|
})
|
|
}
|
|
|
|
// convertToDetails converts repository model to service details model
|
|
func (s *Service) convertToDetails(batch *repository.BatchUpload) *BatchUploadDetails {
|
|
details := &BatchUploadDetails{
|
|
BatchUploadSummary: BatchUploadSummary{
|
|
ID: batch.ID,
|
|
ClientID: batch.ClientID,
|
|
OriginalFilename: batch.OriginalFilename,
|
|
Status: string(batch.Status),
|
|
TotalDocuments: batch.TotalDocuments,
|
|
ProcessedDocuments: batch.ProcessedDocuments,
|
|
FailedDocuments: batch.FailedDocuments,
|
|
InvalidTypeDocuments: batch.InvalidTypeDocuments,
|
|
ProgressPercent: batch.ProgressPercent,
|
|
CreatedAt: batch.CreatedAt.Time,
|
|
},
|
|
FailedFilenames: make([]string, 0),
|
|
}
|
|
|
|
if batch.CompletedAt.Valid {
|
|
details.CompletedAt = &batch.CompletedAt.Time
|
|
}
|
|
|
|
// Parse failed filenames from JSONB
|
|
if len(batch.FailedFilenames) > 0 {
|
|
var filenames []string
|
|
if err := json.Unmarshal(batch.FailedFilenames, &filenames); err == nil {
|
|
details.FailedFilenames = filenames
|
|
}
|
|
}
|
|
|
|
return details
|
|
}
|
|
|
|
// convertToSummary converts repository list row to service summary model
|
|
func (s *Service) convertToSummary(batch *repository.ListBatchUploadsRow) *BatchUploadSummary {
|
|
summary := &BatchUploadSummary{
|
|
ID: batch.ID,
|
|
ClientID: batch.ClientID,
|
|
OriginalFilename: batch.OriginalFilename,
|
|
Status: string(batch.Status),
|
|
TotalDocuments: batch.TotalDocuments,
|
|
ProcessedDocuments: batch.ProcessedDocuments,
|
|
FailedDocuments: batch.FailedDocuments,
|
|
InvalidTypeDocuments: batch.InvalidTypeDocuments,
|
|
ProgressPercent: batch.ProgressPercent,
|
|
CreatedAt: s.pgTimestampToTime(batch.CreatedAt),
|
|
}
|
|
|
|
if batch.CompletedAt.Valid {
|
|
t := s.pgTimestampToTime(batch.CompletedAt)
|
|
summary.CompletedAt = &t
|
|
}
|
|
|
|
return summary
|
|
}
|
|
|
|
// pgTimestampToTime converts pgtype.Timestamp to time.Time
|
|
func (s *Service) pgTimestampToTime(ts pgtype.Timestamp) time.Time {
|
|
if ts.Valid {
|
|
return ts.Time
|
|
}
|
|
return time.Time{}
|
|
}
|