ChartTimeline.coffee 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. class ChartTimeline extends Class
  2. constructor: ->
  3. @items = []
  4. for i in [6..0]
  5. @items.push {id: i, title: "\u200B", data: "\u200B", value: i, active: false}
  6. @active_id = 0
  7. @chart_ctx = null
  8. @need_update = false
  9. @line_data = null
  10. initChart: (node) =>
  11. @chart_canvas = node
  12. @chart_ctx = node.getContext("2d")
  13. if @line_data
  14. @updateChart()
  15. updateChart: =>
  16. @chart_ctx.clearRect(0, 0, @chart_canvas.width, @chart_canvas.height)
  17. @chart_ctx.lineWidth = 0
  18. @chart_ctx.fillStyle = '#EDC54B'
  19. @chart_ctx.beginPath()
  20. @chart_ctx.moveTo(-10,0)
  21. data_max = Math.max.apply(null, @line_data)
  22. data_last_i = (i for val, i in @line_data when val > 0).pop()
  23. line_width = 1400 / @line_data.length
  24. if not data_last_i?
  25. return # No data yet
  26. for data, i in @line_data
  27. line_x = i * line_width
  28. line_y = parseInt(101 - (data / data_max) * 100)
  29. @chart_ctx.lineTo(line_x, line_y)
  30. if i == data_last_i
  31. break
  32. @chart_ctx.lineTo(line_x, 120)
  33. @chart_ctx.lineTo(0, 120)
  34. @chart_ctx.fill()
  35. # Dashed line at the end
  36. if data_last_i > 36
  37. @chart_ctx.beginPath()
  38. @chart_ctx.lineWidth = 0
  39. @chart_ctx.strokeStyle = '#EDC54B'
  40. @chart_ctx.setLineDash [0, 1, 1]
  41. @chart_ctx.moveTo(line_x, line_y)
  42. @chart_ctx.lineTo(1500, line_y)
  43. @chart_ctx.stroke()
  44. update: =>
  45. query = """
  46. SELECT
  47. MAX(date_added) AS date_added, AVG(value) AS avg, SUM(value) AS sum
  48. FROM data
  49. WHERE type_id = :type_id AND date_added >= :date_added_from AND date_added <= :date_added_to
  50. GROUP BY strftime('%Y-%m-%d %H', date_added, 'unixepoch', 'localtime')
  51. ORDER BY date_added DESC
  52. """
  53. if Page.params.interval == "1w"
  54. c = new Date()
  55. c.setDate(c.getDate() - (c.getDay() or 7) + 7)
  56. date_added_to = c.setHours(23,59,59,0) / 1000
  57. interval_step = 60 * 60 * 24 * 7
  58. date_added_from = date_added_to - interval_step * 7
  59. group_steps = 6
  60. else if Page.params.interval == "1m"
  61. c = new Date()
  62. c.setDate(30)
  63. date_added_to = c.setHours(23,59,59,0) / 1000
  64. interval_step = 60 * 60 * 24 * 30
  65. date_added_from = date_added_to - interval_step * 30
  66. group_steps = 24 * 3
  67. else
  68. date_added_to = (new Date()).setHours(23,59,59,0) / 1000
  69. interval_step = 60 * 60 * 24
  70. date_added_from = date_added_to - interval_step * 7
  71. group_steps = 2
  72. step = 60 * 60
  73. type_id = Page.page_stats.type_id_db["file_bytes_sent"]
  74. data = {}
  75. day_total = {}
  76. Page.cmd "chartDbQuery", [query, {type_id: type_id, date_added_from: date_added_from, date_added_to: date_added_to}], (res) =>
  77. @logStart "Parse result", res.length
  78. @line_data = []
  79. for row in res
  80. data[Math.ceil(row.date_added / step) * step] = row.sum
  81. day_string = Time.dateIso(row.date_added * 1000)
  82. day_total[day_string] ?= 0
  83. day_total[day_string] += row.sum
  84. data_date_added = Math.ceil(date_added_from / step) * step
  85. while data_date_added <= date_added_to
  86. group_step_data = 0
  87. for i in [0..group_steps]
  88. group_step_data += data[data_date_added] or 0
  89. data_date_added += step
  90. @line_data.push(group_step_data)
  91. # Update links
  92. @items = []
  93. for i in [7..1]
  94. data_from = date_added_to - i * interval_step + 1
  95. data_to = data_from + interval_step - 1
  96. if Page.params.interval == "1w"
  97. day_data = 0
  98. for x in [0..6]
  99. day_data += day_total[Time.dateIso(data_from + (60 * 60 * 24 * x))] or 0
  100. day_from = Time.date(data_from, "day")
  101. day_to = Time.date(data_from + interval_step - 1, "day")
  102. day_to = day_to.replace(day_from.split(" ")[0], "") # Only display month once if it's the same
  103. day_name = "#{day_from} - #{day_to}"
  104. else if Page.params.interval == "1m"
  105. day_data = 0
  106. for x in [0..30]
  107. day_data += day_total[Time.dateIso(data_from + (60 * 60 * 24 * x))] or 0
  108. day_name = Time.date(data_from, "month")
  109. else
  110. day_data = day_total[Time.dateIso(data_from)]
  111. day_name = Time.weekDay(data_from)
  112. @items.push {id: i, title: day_name, data: day_data, value: data_to}
  113. @logEnd "Parse result", "data: #{@line_data.length}"
  114. Page.projector.scheduleRender()
  115. @updateChart()
  116. renderItem: (item) ->
  117. date_added_to = Time.dateIso(item.value)
  118. if item.value >= Time.timestamp()
  119. date_added_to = ""
  120. classes = {active: (Page.params.date_added_to or "") == date_added_to}
  121. h("a.timeline-item", {key: item.title, enterAnimation: Animation.show, delay: item.id * 0.05, href: Page.createUrl("date_added_to", date_added_to), onclick: Page.handleLinkClick, classes: classes},
  122. h("span.title", item.title),
  123. h("span.data", Text.formatSize(item.data) or "0 MB")
  124. )
  125. render: =>
  126. if @need_update
  127. @update()
  128. @need_update = false
  129. h("div.ChartTimeline", [
  130. h("div.timeline-borders", @items.map (item) =>
  131. date_added_to = Time.dateIso(item.value)
  132. if item.value >= Time.timestamp()
  133. date_added_to = ""
  134. h("div.timeline-border", {key: item.id, classes: {active: (Page.params.date_added_to or "") == date_added_to }})
  135. ),
  136. h("canvas.chart", {afterCreate: @initChart, width: 1400, height: 100, data: @line_data?.length, delay: 0.3, updateAnimation: Animation.show}),
  137. h("div.timeline-items", @items.map (item) =>
  138. @renderItem(item)
  139. )
  140. ])
  141. window.ChartTimeline = ChartTimeline