mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Round language stats percentage using largest remainder (#22026)
Fix #22023 I've changed how the percentages for the language statistics are rounded because they did not always add up to 100% Now it's done with the largest remainder method, which makes sure that total is 100% Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		@@ -6,6 +6,7 @@ package repo
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
@@ -43,7 +44,7 @@ func (stats LanguageStatList) LoadAttributes() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (stats LanguageStatList) getLanguagePercentages() map[string]float32 {
 | 
					func (stats LanguageStatList) getLanguagePercentages() map[string]float32 {
 | 
				
			||||||
	langPerc := make(map[string]float32)
 | 
						langPerc := make(map[string]float32)
 | 
				
			||||||
	var otherPerc float32 = 100
 | 
						var otherPerc float32
 | 
				
			||||||
	var total int64
 | 
						var total int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, stat := range stats {
 | 
						for _, stat := range stats {
 | 
				
			||||||
@@ -51,21 +52,52 @@ func (stats LanguageStatList) getLanguagePercentages() map[string]float32 {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if total > 0 {
 | 
						if total > 0 {
 | 
				
			||||||
		for _, stat := range stats {
 | 
							for _, stat := range stats {
 | 
				
			||||||
			perc := float32(math.Round(float64(stat.Size)/float64(total)*1000) / 10)
 | 
								perc := float32(float64(stat.Size) / float64(total) * 100)
 | 
				
			||||||
			if perc <= 0.1 {
 | 
								if perc <= 0.1 {
 | 
				
			||||||
 | 
									otherPerc += perc
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			otherPerc -= perc
 | 
					 | 
				
			||||||
			langPerc[stat.Language] = perc
 | 
								langPerc[stat.Language] = perc
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		otherPerc = float32(math.Round(float64(otherPerc)*10) / 10)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if otherPerc > 0 {
 | 
						if otherPerc > 0 {
 | 
				
			||||||
		langPerc["other"] = otherPerc
 | 
							langPerc["other"] = otherPerc
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						roundByLargestRemainder(langPerc, 100)
 | 
				
			||||||
	return langPerc
 | 
						return langPerc
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rounds to 1 decimal point, target should be the expected sum of percs
 | 
				
			||||||
 | 
					func roundByLargestRemainder(percs map[string]float32, target float32) {
 | 
				
			||||||
 | 
						leftToDistribute := int(target * 10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(percs))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for k, v := range percs {
 | 
				
			||||||
 | 
							percs[k] = v * 10
 | 
				
			||||||
 | 
							floored := math.Floor(float64(percs[k]))
 | 
				
			||||||
 | 
							leftToDistribute -= int(floored)
 | 
				
			||||||
 | 
							keys = append(keys, k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sort the keys by the largest remainder
 | 
				
			||||||
 | 
						sort.SliceStable(keys, func(i, j int) bool {
 | 
				
			||||||
 | 
							_, remainderI := math.Modf(float64(percs[keys[i]]))
 | 
				
			||||||
 | 
							_, remainderJ := math.Modf(float64(percs[keys[j]]))
 | 
				
			||||||
 | 
							return remainderI > remainderJ
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Increment the values in order of largest remainder
 | 
				
			||||||
 | 
						for _, k := range keys {
 | 
				
			||||||
 | 
							percs[k] = float32(math.Floor(float64(percs[k])))
 | 
				
			||||||
 | 
							if leftToDistribute > 0 {
 | 
				
			||||||
 | 
								percs[k]++
 | 
				
			||||||
 | 
								leftToDistribute--
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							percs[k] /= 10
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetLanguageStats returns the language statistics for a repository
 | 
					// GetLanguageStats returns the language statistics for a repository
 | 
				
			||||||
func GetLanguageStats(ctx context.Context, repo *Repository) (LanguageStatList, error) {
 | 
					func GetLanguageStats(ctx context.Context, repo *Repository) (LanguageStatList, error) {
 | 
				
			||||||
	stats := make(LanguageStatList, 0, 6)
 | 
						stats := make(LanguageStatList, 0, 6)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user