mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
253 lines
7.6 KiB
Go
253 lines
7.6 KiB
Go
package service
|
|
|
|
import (
|
|
"bugulma/backend/internal/domain"
|
|
"bugulma/backend/internal/localization"
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// StatsService provides translation statistics and reporting functionality
|
|
type StatsService struct {
|
|
locRepo domain.LocalizationRepository
|
|
locService domain.LocalizationService
|
|
entityLoader *EntityLoaderService
|
|
}
|
|
|
|
// NewStatsService creates a new stats service
|
|
func NewStatsService(
|
|
locRepo domain.LocalizationRepository,
|
|
locService domain.LocalizationService,
|
|
entityLoader *EntityLoaderService,
|
|
) *StatsService {
|
|
return &StatsService{
|
|
locRepo: locRepo,
|
|
locService: locService,
|
|
entityLoader: entityLoader,
|
|
}
|
|
}
|
|
|
|
// GetTranslationStats returns comprehensive translation statistics
|
|
func (s *StatsService) GetTranslationStats(ctx context.Context, entityTypeFilter string) (*domain.TranslationStats, error) {
|
|
entityTypes := localization.GetEntityTypesForFilter(entityTypeFilter)
|
|
|
|
stats := &domain.TranslationStats{
|
|
EntityStats: make(map[string]*domain.EntityTranslationStats),
|
|
}
|
|
|
|
totalEntities := 0
|
|
totalFields := 0
|
|
totalTranslations := 0
|
|
|
|
for _, entityType := range entityTypes {
|
|
entityStats, err := s.getEntityTranslationStats(ctx, entityType)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
stats.EntityStats[entityType] = entityStats
|
|
totalEntities += entityStats.TotalEntities
|
|
totalFields += entityStats.TotalFields
|
|
totalTranslations += entityStats.TotalTranslations
|
|
}
|
|
|
|
stats.TotalEntities = totalEntities
|
|
stats.TotalFields = totalFields
|
|
stats.TotalTranslations = totalTranslations
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetUntranslatedFields returns fields that need translation
|
|
func (s *StatsService) GetUntranslatedFields(ctx context.Context, entityTypeFilter, targetLocale string) ([]UntranslatedField, error) {
|
|
entityTypes := localization.GetEntityTypesForFilter(entityTypeFilter)
|
|
var results []UntranslatedField
|
|
|
|
for _, entityType := range entityTypes {
|
|
entities, err := s.entityLoader.LoadEntities(entityType, localization.LoadOptions{})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
fields := localization.GetFieldsForEntityType(entityType)
|
|
|
|
for _, entity := range entities {
|
|
entityID := s.entityLoader.GetEntityID(entity)
|
|
|
|
for _, field := range fields {
|
|
russianText := s.entityLoader.GetRussianContent(entity, field)
|
|
if russianText == "" {
|
|
continue
|
|
}
|
|
|
|
// Check if translation exists
|
|
existing, err := s.locService.GetLocalizedValue(entityType, entityID, field, targetLocale)
|
|
if err != nil || existing == "" {
|
|
results = append(results, UntranslatedField{
|
|
EntityType: entityType,
|
|
EntityID: entityID,
|
|
Field: field,
|
|
RussianText: russianText,
|
|
Locale: targetLocale,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
// UntranslatedField represents a field that needs translation
|
|
type UntranslatedField struct {
|
|
EntityType string
|
|
EntityID string
|
|
Field string
|
|
RussianText string
|
|
Locale string
|
|
}
|
|
|
|
// GetCoverageReport returns a detailed coverage report
|
|
func (s *StatsService) GetCoverageReport(ctx context.Context, entityTypeFilter string) (*CoverageReport, error) {
|
|
stats, err := s.GetTranslationStats(ctx, entityTypeFilter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
report := &CoverageReport{
|
|
TotalEntities: stats.TotalEntities,
|
|
TotalFields: stats.TotalFields,
|
|
TotalTranslations: stats.TotalTranslations,
|
|
EntityReports: make(map[string]*EntityCoverageReport),
|
|
}
|
|
|
|
// Calculate overall coverage
|
|
if stats.TotalFields > 0 {
|
|
report.OverallCoverage = float64(stats.TotalTranslations) / float64(stats.TotalFields) * 100
|
|
}
|
|
|
|
// Generate entity-specific reports
|
|
for entityType, entityStats := range stats.EntityStats {
|
|
entityReport := &EntityCoverageReport{
|
|
EntityType: entityType,
|
|
TotalEntities: entityStats.TotalEntities,
|
|
TotalFields: entityStats.TotalFields,
|
|
RussianCount: entityStats.RussianCount,
|
|
EnglishCount: entityStats.EnglishCount,
|
|
TatarCount: entityStats.TatarCount,
|
|
}
|
|
|
|
if entityStats.TotalFields > 0 {
|
|
entityReport.FieldCoverage = float64(entityStats.TotalTranslations) / float64(entityStats.TotalFields) * 100
|
|
}
|
|
|
|
report.EntityReports[entityType] = entityReport
|
|
}
|
|
|
|
return report, nil
|
|
}
|
|
|
|
// CoverageReport represents a comprehensive coverage report
|
|
type CoverageReport struct {
|
|
TotalEntities int
|
|
TotalFields int
|
|
TotalTranslations int
|
|
OverallCoverage float64
|
|
EntityReports map[string]*EntityCoverageReport
|
|
}
|
|
|
|
// EntityCoverageReport represents coverage for a specific entity type
|
|
type EntityCoverageReport struct {
|
|
EntityType string
|
|
TotalEntities int
|
|
TotalFields int
|
|
RussianCount int
|
|
EnglishCount int
|
|
TatarCount int
|
|
FieldCoverage float64
|
|
}
|
|
|
|
// GetTranslationSummary returns a formatted summary string
|
|
func (s *StatsService) GetTranslationSummary(ctx context.Context, entityTypeFilter string) (string, error) {
|
|
stats, err := s.GetTranslationStats(ctx, entityTypeFilter)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
summary := fmt.Sprintf("📊 Translation Statistics for: %s\n\n", entityTypeFilter)
|
|
|
|
entityTypes := localization.GetEntityTypesForFilter(entityTypeFilter)
|
|
|
|
for _, entityType := range entityTypes {
|
|
if entityStats, exists := stats.EntityStats[entityType]; exists {
|
|
summary += fmt.Sprintf("📋 %s Statistics:\n", entityType)
|
|
summary += fmt.Sprintf(" 📊 Entities: %d\n", entityStats.TotalEntities)
|
|
summary += fmt.Sprintf(" 📝 Fields per entity: %d\n", entityStats.TotalFields/entityStats.TotalEntities)
|
|
summary += fmt.Sprintf(" 🌍 Complete Translations: %d/%d (%.1f%% coverage)\n",
|
|
entityStats.TotalTranslations, entityStats.TotalFields,
|
|
float64(entityStats.TotalTranslations)/float64(entityStats.TotalFields)*100)
|
|
|
|
if entityStats.TotalTranslations > 0 {
|
|
summary += fmt.Sprintf(" 📈 By locale: ru:%d en:%d tt:%d\n",
|
|
entityStats.RussianCount, entityStats.EnglishCount, entityStats.TatarCount)
|
|
}
|
|
summary += "\n"
|
|
}
|
|
}
|
|
|
|
if len(entityTypes) > 1 {
|
|
summary += fmt.Sprintf("📈 Overall Statistics:\n")
|
|
summary += fmt.Sprintf(" 📊 Total Entities: %d\n", stats.TotalEntities)
|
|
summary += fmt.Sprintf(" 📝 Total Fields: %d\n", stats.TotalFields)
|
|
summary += fmt.Sprintf(" 🌍 Complete Entity Translations: %d\n", stats.TotalTranslations)
|
|
if stats.TotalFields > 0 {
|
|
summary += fmt.Sprintf(" ✅ Overall Coverage: %.1f%%\n",
|
|
float64(stats.TotalTranslations)/float64(stats.TotalFields)*100)
|
|
}
|
|
}
|
|
|
|
return summary, nil
|
|
}
|
|
|
|
// getEntityTranslationStats gets stats for a specific entity type
|
|
func (s *StatsService) getEntityTranslationStats(ctx context.Context, entityType string) (*domain.EntityTranslationStats, error) {
|
|
entities, err := s.entityLoader.LoadEntities(entityType, localization.LoadOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fields := localization.GetFieldsForEntityType(entityType)
|
|
totalFields := len(entities) * len(fields)
|
|
|
|
// Count translations by locale
|
|
ruCount := 0
|
|
enCount := 0
|
|
ttCount := 0
|
|
|
|
for _, entity := range entities {
|
|
entityID := s.entityLoader.GetEntityID(entity)
|
|
|
|
for _, field := range fields {
|
|
if ruVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "ru"); ruVal != "" {
|
|
ruCount++
|
|
}
|
|
if enVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "en"); enVal != "" {
|
|
enCount++
|
|
}
|
|
if ttVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "tt"); ttVal != "" {
|
|
ttCount++
|
|
}
|
|
}
|
|
}
|
|
|
|
return &domain.EntityTranslationStats{
|
|
EntityType: entityType,
|
|
TotalEntities: len(entities),
|
|
TotalFields: totalFields,
|
|
RussianCount: ruCount,
|
|
EnglishCount: enCount,
|
|
TatarCount: ttCount,
|
|
TotalTranslations: ruCount + enCount + ttCount,
|
|
}, nil
|
|
}
|