primitives.go 4.1 KB

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