primitives.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package feed
  2. import (
  3. "math"
  4. "sort"
  5. "time"
  6. )
  7. type ForumPost struct {
  8. Title string
  9. DiscussionUrl string
  10. TargetUrl string
  11. TargetUrlDomain string
  12. ThumbnailUrl string
  13. CommentCount int
  14. Score int
  15. Engagement float64
  16. TimePosted time.Time
  17. Tags []string
  18. }
  19. type ForumPosts []ForumPost
  20. type Calendar struct {
  21. CurrentDay int
  22. CurrentWeekNumber int
  23. CurrentMonthName string
  24. CurrentYear int
  25. Days []int
  26. }
  27. type Weather struct {
  28. Temperature int
  29. ApparentTemperature int
  30. WeatherCode int
  31. CurrentColumn int
  32. SunriseColumn int
  33. SunsetColumn int
  34. Columns []weatherColumn
  35. }
  36. type AppRelease struct {
  37. Name string
  38. Version string
  39. NotesUrl string
  40. TimeReleased time.Time
  41. Downvotes int
  42. }
  43. type AppReleases []AppRelease
  44. type Video struct {
  45. ThumbnailUrl string
  46. Title string
  47. Url string
  48. Author string
  49. AuthorUrl string
  50. TimePosted time.Time
  51. }
  52. type Videos []Video
  53. var currencyToSymbol = map[string]string{
  54. "USD": "$",
  55. "EUR": "€",
  56. "JPY": "¥",
  57. "CAD": "C$",
  58. "AUD": "A$",
  59. "GBP": "£",
  60. "CHF": "Fr",
  61. "NZD": "N$",
  62. "INR": "₹",
  63. "BRL": "R$",
  64. "RUB": "₽",
  65. "TRY": "₺",
  66. "ZAR": "R",
  67. "CNY": "¥",
  68. "KRW": "₩",
  69. "HKD": "HK$",
  70. "SGD": "S$",
  71. "SEK": "kr",
  72. "NOK": "kr",
  73. "DKK": "kr",
  74. "PLN": "zł",
  75. "PHP": "₱",
  76. }
  77. type Stock struct {
  78. Name string
  79. Symbol string
  80. Currency string
  81. Price float64
  82. PercentChange float64
  83. SvgChartPoints string
  84. }
  85. type Stocks []Stock
  86. func (t Stocks) SortByAbsChange() {
  87. sort.Slice(t, func(i, j int) bool {
  88. return math.Abs(t[i].PercentChange) > math.Abs(t[j].PercentChange)
  89. })
  90. }
  91. var weatherCodeTable = map[int]string{
  92. 0: "Clear Sky",
  93. 1: "Mainly Clear",
  94. 2: "Partly Cloudy",
  95. 3: "Overcast",
  96. 45: "Fog",
  97. 48: "Rime Fog",
  98. 51: "Drizzle",
  99. 53: "Drizzle",
  100. 55: "Drizzle",
  101. 56: "Drizzle",
  102. 57: "Drizzle",
  103. 61: "Rain",
  104. 63: "Moderate Rain",
  105. 65: "Heavy Rain",
  106. 66: "Freezing Rain",
  107. 67: "Freezing Rain",
  108. 71: "Snow",
  109. 73: "Moderate Snow",
  110. 75: "Heavy Snow",
  111. 77: "Snow Grains",
  112. 80: "Rain",
  113. 81: "Moderate Rain",
  114. 82: "Heavy Rain",
  115. 85: "Snow",
  116. 86: "Snow",
  117. 95: "Thunderstorm",
  118. 96: "Thunderstorm",
  119. 99: "Thunderstorm",
  120. }
  121. func (w *Weather) WeatherCodeAsString() string {
  122. if weatherCode, ok := weatherCodeTable[w.WeatherCode]; ok {
  123. return weatherCode
  124. }
  125. return ""
  126. }
  127. const depreciatePostsOlderThanHours = 7
  128. const maxDepreciation = 0.9
  129. const maxDepreciationAfterHours = 24
  130. func (p ForumPosts) CalculateEngagement() {
  131. var totalComments int
  132. var totalScore int
  133. for i := range p {
  134. totalComments += p[i].CommentCount
  135. totalScore += p[i].Score
  136. }
  137. numberOfPosts := float64(len(p))
  138. averageComments := float64(totalComments) / numberOfPosts
  139. averageScore := float64(totalScore) / numberOfPosts
  140. for i := range p {
  141. p[i].Engagement = (float64(p[i].CommentCount)/averageComments + float64(p[i].Score)/averageScore) / 2
  142. elapsed := time.Since(p[i].TimePosted)
  143. if elapsed < time.Hour*depreciatePostsOlderThanHours {
  144. continue
  145. }
  146. p[i].Engagement *= 1.0 - (math.Max(elapsed.Hours()-depreciatePostsOlderThanHours, maxDepreciationAfterHours)/maxDepreciationAfterHours)*maxDepreciation
  147. }
  148. }
  149. func (p ForumPosts) SortByEngagement() {
  150. sort.Slice(p, func(i, j int) bool {
  151. return p[i].Engagement > p[j].Engagement
  152. })
  153. }
  154. func (s *ForumPost) HasTargetUrl() bool {
  155. return s.TargetUrl != ""
  156. }
  157. func (p ForumPosts) FilterPostedBefore(postedBefore time.Duration) []ForumPost {
  158. recent := make([]ForumPost, 0, len(p))
  159. for i := range p {
  160. if time.Since(p[i].TimePosted) < postedBefore {
  161. recent = append(recent, p[i])
  162. }
  163. }
  164. return recent
  165. }
  166. func (r AppReleases) SortByNewest() AppReleases {
  167. sort.Slice(r, func(i, j int) bool {
  168. return r[i].TimeReleased.After(r[j].TimeReleased)
  169. })
  170. return r
  171. }
  172. func (v Videos) SortByNewest() Videos {
  173. sort.Slice(v, func(i, j int) bool {
  174. return v[i].TimePosted.After(v[j].TimePosted)
  175. })
  176. return v
  177. }