primitives.go 4.7 KB

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