Refactor tender recommendation response handling and improve test coverage
continuous-integration/drone/push Build is passing

- Renamed the test function to `TestBuildRecommendedTenderListResponsesPreservesOrderWithoutStorage` for clarity and updated its logic to reflect changes in the recommendation response building process.
- Removed unnecessary sleep in the test to enhance performance.
- Refactored the `buildRecommendedTenderResponsesParallel` method to `buildRecommendedTenderListResponses`, simplifying the response building without relying on MinIO translation lookups.
- Updated the repository's `mapProcedureReferencesToTenders` function to improve efficiency by grouping candidates by their contract folder IDs, enhancing the mapping process.

This update streamlines the recommendation response handling and enhances the test suite for better maintainability and performance.
This commit is contained in:
Mazyar
2026-07-08 01:23:21 +03:30
parent adaae4c4bc
commit 2980a3beb4
4 changed files with 35 additions and 57 deletions
+9 -44
View File
@@ -6,13 +6,9 @@ import (
"strings"
"sync"
"golang.org/x/sync/errgroup"
"tm/pkg/ai_summarizer"
)
const recommendationTranslationConcurrency = 8
type rankedRecommendedTender struct {
tender Tender
rank int
@@ -54,52 +50,21 @@ func (c *recommendationTranslationCache) set(tenderID, language string, stored a
c.items[recommendationTranslationCacheKey(tenderID, language)] = stored
}
func (s *tenderService) buildRecommendedTenderResponsesParallel(
ctx context.Context,
items []rankedRecommendedTender,
language string,
) []TenderResponse {
// buildRecommendedTenderListResponses builds list responses without MinIO translation lookups.
// Recommendation pages use MongoDB text; tender detail endpoints still enrich from storage.
func (s *tenderService) buildRecommendedTenderListResponses(items []rankedRecommendedTender, language string) []TenderResponse {
if len(items) == 0 {
return nil
}
out := make([]TenderResponse, len(items))
if len(items) == 1 {
out[0] = s.buildRecommendedTenderResponse(ctx, items[0].tender, items[0].rank, items[0].analysis, items[0].tenderRef, language, newRecommendationTranslationCache())
return out
for i, item := range items {
resp := item.tender.ToResponseWithLanguage(language)
resp.Rank = item.rank
resp.Analysis = item.analysis
resp.ProcedureRef = item.tenderRef
out[i] = *resp
}
translationCache := newRecommendationTranslationCache()
group, groupCtx := errgroup.WithContext(ctx)
group.SetLimit(recommendationTranslationConcurrency)
for i := range items {
i := i
item := items[i]
group.Go(func() error {
out[i] = s.buildRecommendedTenderResponse(
groupCtx,
item.tender,
item.rank,
item.analysis,
item.tenderRef,
language,
translationCache,
)
return nil
})
}
if err := group.Wait(); err != nil {
s.logger.Warn("Parallel recommendation response build degraded to sequential", map[string]interface{}{
"error": err.Error(),
"count": len(items),
})
for i, item := range items {
out[i] = s.buildRecommendedTenderResponse(ctx, item.tender, item.rank, item.analysis, item.tenderRef, language, translationCache)
}
}
return out
}