analytics_service_calculations.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package nginx_log
  2. import (
  3. "sort"
  4. "time"
  5. )
  6. // calculateHourlyStats calculates UV/PV statistics for each hour of the day
  7. func (s *AnalyticsService) calculateHourlyStats(entries []*AccessLogEntry, startTime, endTime int64) []HourlyAccessStats {
  8. // Create map to aggregate stats by hour (0-23)
  9. hourStats := make(map[int]map[string]bool) // hour -> set of unique IPs
  10. hourPV := make(map[int]int) // hour -> page view count
  11. // Initialize all 24 hours
  12. for i := 0; i < 24; i++ {
  13. hourStats[i] = make(map[string]bool)
  14. hourPV[i] = 0
  15. }
  16. // Process entries
  17. for _, entry := range entries {
  18. entryTime := time.Unix(entry.Timestamp, 0)
  19. hour := entryTime.Hour()
  20. // Count unique visitors (UV)
  21. hourStats[hour][entry.IP] = true
  22. // Count page views (PV)
  23. hourPV[hour]++
  24. }
  25. // Convert to result format - always return 24 hours
  26. result := make([]HourlyAccessStats, 0, 24)
  27. for hour := 0; hour < 24; hour++ {
  28. // Create timestamp for this hour today
  29. now := time.Now()
  30. hourTime := time.Date(now.Year(), now.Month(), now.Day(), hour, 0, 0, 0, now.Location())
  31. result = append(result, HourlyAccessStats{
  32. Hour: hour,
  33. UV: len(hourStats[hour]),
  34. PV: hourPV[hour],
  35. Timestamp: hourTime.Unix(),
  36. })
  37. }
  38. return result
  39. }
  40. // calculateDailyStats calculates daily UV/PV statistics for the time range with padding
  41. func (s *AnalyticsService) calculateDailyStats(entries []*AccessLogEntry, startTime, endTime int64) []DailyAccessStats {
  42. // Create map to aggregate stats by date
  43. dailyStats := make(map[string]map[string]bool) // date -> set of unique IPs
  44. dailyPV := make(map[string]int) // date -> page view count
  45. // Process entries
  46. for _, entry := range entries {
  47. entryTime := time.Unix(entry.Timestamp, 0)
  48. date := entryTime.Format("2006-01-02")
  49. if dailyStats[date] == nil {
  50. dailyStats[date] = make(map[string]bool)
  51. }
  52. // Count unique visitors
  53. dailyStats[date][entry.IP] = true
  54. // Count page views
  55. dailyPV[date]++
  56. }
  57. // Generate complete date range with padding
  58. result := make([]DailyAccessStats, 0)
  59. // Use default time range if not provided
  60. var startDateTime, endDateTime time.Time
  61. if startTime == 0 || endTime == 0 {
  62. endDateTime = time.Now()
  63. startDateTime = endDateTime.AddDate(0, 0, -30) // 30 days ago
  64. } else {
  65. startDateTime = time.Unix(startTime, 0)
  66. endDateTime = time.Unix(endTime, 0)
  67. }
  68. currentDate := startDateTime.Truncate(24 * time.Hour)
  69. for currentDate.Before(endDateTime) || currentDate.Equal(endDateTime.Truncate(24*time.Hour)) {
  70. dateKey := currentDate.Format("2006-01-02")
  71. if ips, exists := dailyStats[dateKey]; exists {
  72. result = append(result, DailyAccessStats{
  73. Date: dateKey,
  74. UV: len(ips),
  75. PV: dailyPV[dateKey],
  76. Timestamp: currentDate.Unix(),
  77. })
  78. } else {
  79. // Pad with zeros for dates without data
  80. result = append(result, DailyAccessStats{
  81. Date: dateKey,
  82. UV: 0,
  83. PV: 0,
  84. Timestamp: currentDate.Unix(),
  85. })
  86. }
  87. currentDate = currentDate.AddDate(0, 0, 1)
  88. }
  89. return result
  90. }
  91. // calculateTopURLs calculates the most visited URLs
  92. func (s *AnalyticsService) calculateTopURLs(entries []*AccessLogEntry) []URLAccessStats {
  93. urlCount := make(map[string]int)
  94. totalRequests := len(entries)
  95. // Count URL visits
  96. for _, entry := range entries {
  97. urlCount[entry.Path]++
  98. }
  99. // Convert to slice and sort
  100. var urlStats []URLAccessStats
  101. for url, count := range urlCount {
  102. percent := 0.0
  103. if totalRequests > 0 {
  104. percent = float64(count) * 100.0 / float64(totalRequests)
  105. }
  106. urlStats = append(urlStats, URLAccessStats{
  107. URL: url,
  108. Visits: count,
  109. Percent: percent,
  110. })
  111. }
  112. // Sort by visits (descending)
  113. sort.Slice(urlStats, func(i, j int) bool {
  114. return urlStats[i].Visits > urlStats[j].Visits
  115. })
  116. // Limit to top 10
  117. if len(urlStats) > 10 {
  118. urlStats = urlStats[:10]
  119. }
  120. return urlStats
  121. }
  122. // calculateBrowserStats calculates browser usage statistics
  123. func (s *AnalyticsService) calculateBrowserStats(entries []*AccessLogEntry) []BrowserAccessStats {
  124. commonStats := calculateCommonStats(entries, func(entry *AccessLogEntry) string {
  125. return entry.Browser
  126. })
  127. // Convert to BrowserAccessStats format
  128. result := make([]BrowserAccessStats, len(commonStats))
  129. for i, stat := range commonStats {
  130. result[i] = BrowserAccessStats{
  131. Browser: stat.Name,
  132. Count: stat.Count,
  133. Percent: stat.Percent,
  134. }
  135. }
  136. return result
  137. }
  138. // calculateOSStats calculates operating system usage statistics
  139. func (s *AnalyticsService) calculateOSStats(entries []*AccessLogEntry) []OSAccessStats {
  140. commonStats := calculateCommonStats(entries, func(entry *AccessLogEntry) string {
  141. return entry.OS
  142. })
  143. // Convert to OSAccessStats format
  144. result := make([]OSAccessStats, len(commonStats))
  145. for i, stat := range commonStats {
  146. result[i] = OSAccessStats{
  147. OS: stat.Name,
  148. Count: stat.Count,
  149. Percent: stat.Percent,
  150. }
  151. }
  152. return result
  153. }
  154. // calculateDeviceStats calculates device type usage statistics
  155. func (s *AnalyticsService) calculateDeviceStats(entries []*AccessLogEntry) []DeviceAccessStats {
  156. commonStats := calculateCommonStats(entries, func(entry *AccessLogEntry) string {
  157. return entry.DeviceType
  158. })
  159. // Convert to DeviceAccessStats format
  160. result := make([]DeviceAccessStats, len(commonStats))
  161. for i, stat := range commonStats {
  162. result[i] = DeviceAccessStats{
  163. Device: stat.Name,
  164. Count: stat.Count,
  165. Percent: stat.Percent,
  166. }
  167. }
  168. return result
  169. }