150 lines
4.4 KiB
Go
150 lines
4.4 KiB
Go
package workers
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
"tm/internal/tender"
|
|
"tm/pkg/ai_summarizer"
|
|
"tm/pkg/logger"
|
|
"tm/pkg/mongo"
|
|
)
|
|
|
|
// TranslationWorker backfills missing tender translations using AI summarizer API.
|
|
type TranslationWorker struct {
|
|
Mongo *mongo.ConnectionManager
|
|
Logger logger.Logger
|
|
TenderRepo tender.TenderRepository
|
|
AIClient *ai_summarizer.Client
|
|
TargetLanguage string
|
|
BatchSize int
|
|
}
|
|
|
|
// NewTranslationWorker creates a translation worker for a target language.
|
|
func NewTranslationWorker(mongo *mongo.ConnectionManager, logger logger.Logger, tenderRepo tender.TenderRepository, aiClient *ai_summarizer.Client, targetLanguage string, batchSize int) *TranslationWorker {
|
|
if strings.TrimSpace(targetLanguage) == "" {
|
|
targetLanguage = "en"
|
|
}
|
|
if batchSize <= 0 {
|
|
batchSize = 20
|
|
}
|
|
|
|
return &TranslationWorker{
|
|
Mongo: mongo,
|
|
Logger: logger,
|
|
TenderRepo: tenderRepo,
|
|
AIClient: aiClient,
|
|
TargetLanguage: strings.ToLower(strings.TrimSpace(targetLanguage)),
|
|
BatchSize: batchSize,
|
|
}
|
|
}
|
|
|
|
// Run starts backfilling translations for tenders missing target-language content.
|
|
func (w *TranslationWorker) Run() {
|
|
if w.AIClient == nil {
|
|
w.Logger.Warn("Translation worker skipped: AI client is not configured", map[string]interface{}{
|
|
"target_language": w.TargetLanguage,
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Logger.Info("Translation worker started", map[string]interface{}{
|
|
"target_language": w.TargetLanguage,
|
|
"batch_size": w.BatchSize,
|
|
})
|
|
|
|
for {
|
|
// Always use skip=0 because successfully translated tenders are excluded by filter.
|
|
tenders, totalCount, err := w.TenderRepo.GetUnTranslatedTenders(context.Background(), w.TargetLanguage, w.BatchSize, 0)
|
|
if err != nil {
|
|
w.Logger.Error("Failed to fetch untranslated tenders", map[string]interface{}{
|
|
"target_language": w.TargetLanguage,
|
|
"error": err.Error(),
|
|
})
|
|
break
|
|
}
|
|
|
|
w.Logger.Info("Fetched untranslated tenders batch", map[string]interface{}{
|
|
"target_language": w.TargetLanguage,
|
|
"batch_count": len(tenders),
|
|
"total_count": totalCount,
|
|
})
|
|
|
|
if len(tenders) == 0 {
|
|
break
|
|
}
|
|
|
|
for _, t := range tenders {
|
|
w.translateTender(&t)
|
|
}
|
|
}
|
|
|
|
w.Logger.Info("Translation worker completed", map[string]interface{}{
|
|
"target_language": w.TargetLanguage,
|
|
})
|
|
}
|
|
|
|
func (w *TranslationWorker) translateTender(t *tender.Tender) {
|
|
if t.NoticePublicationID == "" {
|
|
return
|
|
}
|
|
if existing, ok := t.Translations[w.TargetLanguage]; ok && strings.TrimSpace(existing.Title) != "" {
|
|
w.Logger.Debug("Skipping tender translation because target language already exists", map[string]interface{}{
|
|
"tender_id": t.ID.Hex(),
|
|
"notice_id": t.NoticePublicationID,
|
|
"target_language": w.TargetLanguage,
|
|
})
|
|
return
|
|
}
|
|
|
|
resp, err := w.AIClient.FetchTranslationOnDemand(context.Background(), ai_summarizer.TranslateRequest{
|
|
NoticeID: t.NoticePublicationID,
|
|
Title: t.Title,
|
|
Description: t.Description,
|
|
Language: w.TargetLanguage,
|
|
})
|
|
if err != nil {
|
|
w.Logger.Error("Failed to translate tender", map[string]interface{}{
|
|
"tender_id": t.ID.Hex(),
|
|
"notice_id": t.NoticePublicationID,
|
|
"target_language": w.TargetLanguage,
|
|
"translation_error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
if t.Translations == nil {
|
|
t.Translations = make(map[string]tender.TranslationEntry)
|
|
}
|
|
now := time.Now().Unix()
|
|
// Only update the target-language slot. Existing translations in other
|
|
// languages (e.g., "de", "fr") are preserved and never modified.
|
|
t.Translations[w.TargetLanguage] = tender.TranslationEntry{
|
|
Title: resp.TranslatedTitle,
|
|
Description: resp.TranslatedDescription,
|
|
Language: w.TargetLanguage,
|
|
TranslatedAt: now,
|
|
}
|
|
if t.ProcessingMetadata.TranslatedData == nil {
|
|
t.ProcessingMetadata.TranslatedData = make(map[string]string)
|
|
}
|
|
t.ProcessingMetadata.TranslatedData[w.TargetLanguage] = "done"
|
|
t.ProcessingMetadata.TranslatedAt = now
|
|
|
|
if err := w.TenderRepo.Update(context.Background(), t); err != nil {
|
|
w.Logger.Error("Failed to persist translated tender", map[string]interface{}{
|
|
"tender_id": t.ID.Hex(),
|
|
"notice_id": t.NoticePublicationID,
|
|
"target_language": w.TargetLanguage,
|
|
"persistence_error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Logger.Info("Tender translated successfully", map[string]interface{}{
|
|
"tender_id": t.ID.Hex(),
|
|
"notice_id": t.NoticePublicationID,
|
|
"target_language": w.TargetLanguage,
|
|
})
|
|
}
|