Skip to content

Commit

Permalink
16 highcharts (#21)
Browse files Browse the repository at this point in the history
* Tweet metric methods

* Add TweetsController with views

* Linting fix

* Add charts

* Sort Gemfile

* Align side title with vh

* Linting fix

* Renove redundand highsharts script from the app template
  • Loading branch information
hoblin authored Oct 7, 2023
1 parent b9d2dba commit 81a8508
Show file tree
Hide file tree
Showing 21 changed files with 593 additions and 31 deletions.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ gem "cssbundling-rails"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]

gem "awesome_print", "~> 1.9"
gem "chartkick", "~> 5.0"
gem "data_migrate", "~> 9.2"
gem "draper", "~> 4.0"
gem "groupdate", "~> 6.4"
gem "pry", "~> 0.14.2"
gem "slim", "~> 5.1"
gem "sqlite3", "~> 1.6"
Expand Down
19 changes: 19 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ GEM
globalid (>= 0.3.6)
activemodel (7.1.0)
activesupport (= 7.1.0)
activemodel-serializers-xml (1.0.2)
activemodel (> 5.x)
activesupport (> 5.x)
builder (~> 3.1)
activerecord (7.1.0)
activemodel (= 7.1.0)
activesupport (= 7.1.0)
Expand Down Expand Up @@ -89,6 +93,7 @@ GEM
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
builder (3.2.4)
chartkick (5.0.4)
coderay (1.1.3)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
Expand All @@ -104,6 +109,13 @@ GEM
reline (>= 0.3.1)
debug_inspector (1.1.0)
diff-lcs (1.5.0)
draper (4.0.2)
actionpack (>= 5.0)
activemodel (>= 5.0)
activemodel-serializers-xml (>= 1.0)
activesupport (>= 5.0)
request_store (>= 1.0)
ruby2_keywords
drb (2.1.1)
ruby2_keywords
erubi (1.12.0)
Expand All @@ -118,6 +130,8 @@ GEM
ruby-progressbar (~> 1.4)
globalid (1.2.1)
activesupport (>= 6.1)
groupdate (6.4.0)
activesupport (>= 6.1)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
io-console (0.6.0)
Expand Down Expand Up @@ -218,6 +232,8 @@ GEM
regexp_parser (2.8.1)
reline (0.3.9)
io-console (~> 0.5)
request_store (1.5.1)
rack (>= 1.4)
rexml (3.2.6)
rouge (4.1.3)
rspec-core (3.12.2)
Expand Down Expand Up @@ -318,12 +334,15 @@ DEPENDENCIES
awesome_print (~> 1.9)
better_errors (~> 2.10)
binding_of_caller (~> 1.0)
chartkick (~> 5.0)
cssbundling-rails
data_migrate (~> 9.2)
debug
draper (~> 4.0)
factory_bot_rails (~> 6.2)
ffaker (~> 2.23)
fuubar (~> 2.5)
groupdate (~> 6.4)
jsbundling-rails
ordinare (~> 0.4.0)
pg (~> 1.1)
Expand Down
21 changes: 21 additions & 0 deletions app/assets/stylesheets/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,24 @@
.title {
font-family: Source Code Pro;
}

h1.rotated {
transform: rotate(-90deg);
transform-origin: bottom left;
white-space: nowrap;
position: absolute;
bottom: 30vh;
left: 9vw;
font-family: Source Code Pro;
font-size: 3rem;
}

@media (max-width: 768px) {
h1.rotated {
transform: none;
position: static;
bottom: auto;
left: auto;
font-size: 2rem;
}
}
21 changes: 21 additions & 0 deletions app/controllers/tweets_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class TweetsController < ApplicationController
def index
@tweets = Tweet.first(10).map(&:decorate)

respond_to do |format|
format.html # renders index.html.slim
end
end

def show
@tweet = Tweet.find(params[:id]).decorate

respond_to do |format|
format.html # renders show.html.slim
format.json do
# TODO: add filtering by date range and by metrics subset
render json: @tweet.tweet_metrics.to_json
end
end
end
end
8 changes: 8 additions & 0 deletions app/decorators/application_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class ApplicationDecorator < Draper::Decorator
# Define methods for all decorated objects.
# Helpers are accessed through `helpers` (aka `h`). For example:
#
# def percent_amount
# h.number_to_percentage object.amount, precision: 2
# end
end
120 changes: 120 additions & 0 deletions app/decorators/tweet_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
class TweetDecorator < Draper::Decorator
delegate_all

def combined_chart
h.area_chart combined_metrics_series, height: "20vh", library: chart_options
end

def likes_chart
h.line_chart(
likes_metric[:data],
min: min_y(likes_metric),
max: max_y(likes_metric),
height: "70vh",
library: single_metric_chart_options("Likes")
)
end

def replies_chart
h.line_chart(
replies_metric[:data],
min: min_y(replies_metric),
max: max_y(replies_metric),
height: "70vh",
library: single_metric_chart_options("Replies")
)
end

def reposts_chart
h.line_chart(
reposts_metric[:data],
min: min_y(reposts_metric),
max: max_y(reposts_metric),
height: "70vh",
library: single_metric_chart_options("Reposts")
)
end

def bookmarks_chart
h.line_chart(
bookmarks_metric[:data],
min: min_y(bookmarks_metric),
max: max_y(bookmarks_metric),
height: "70vh",
library: single_metric_chart_options("Bookmarks")
)
end

def views_chart
h.line_chart(
views_metric[:data],
min: min_y(views_metric),
max: max_y(views_metric),
height: "70vh",
library: single_metric_chart_options("Views")
)
end

private

def min_y(metric)
metric[:data].min_by { |k, v| k.to_i }&.last
end

def max_y(metric)
metric[:data].max_by { |k, v| k.to_i }&.last
end

def single_metric_chart_options(title)
chart_options.merge(
yAxis: {
title: {
text: title
}
}
)
end

def chart_options
{
chart: {
zoomType: "xy"
},
resetZoomButton: {
theme: {
display: "flex"
}
}
}
end

def combined_metrics_series
[
likes_metric,
replies_metric,
reposts_metric,
bookmarks_metric,
views_metric
]
end

def likes_metric
@likes_metric ||= {name: "Likes", data: object.tweet_metrics.group_by_minute(:created_at).maximum(:likes)}
end

def replies_metric
@replies_metric ||= {name: "Replies", data: object.tweet_metrics.group_by_minute(:created_at).maximum(:replies)}
end

def reposts_metric
@reposts_metric ||= {name: "Reposts", data: object.tweet_metrics.group_by_minute(:created_at).maximum(:reposts)}
end

def bookmarks_metric
@bookmarks_metric ||= {name: "Bookmarks", data: object.tweet_metrics.group_by_minute(:created_at).maximum(:bookmarks)}
end

def views_metric
@views_metric ||= {name: "Views", data: object.tweet_metrics.group_by_minute(:created_at).maximum(:views)}
end
end
1 change: 1 addition & 0 deletions app/javascript/application.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "./controllers"
import "chartkick/highcharts"
42 changes: 42 additions & 0 deletions app/models/tweet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,50 @@ class Tweet < ApplicationRecord

before_validation :set_author

def author_avatar_url
# TODO: Implement a way to get the avatar url from the tweet
"https://pbs.twimg.com/profile_images/1678305940481753089/g751T5c__400x400.jpg"
end

def author_url
"https://twitter.com/#{author}"
end

def author_name
"@#{author}"
end

def likes
last_metric&.likes
end

def reposts
last_metric&.reposts
end

def replies
last_metric&.replies
end

# TODO: Add quotes to the tweet metrics
# def quotes
# last_metric&.quotes
# end

def bookmarks
last_metric&.bookmarks
end

def views
last_metric&.views
end

private

def last_metric
@last_metric ||= tweet_metrics.last
end

def set_author
self.author = url&.split("/")&.fetch(3)
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ html
= stylesheet_link_tag 'application', 'data-turbo-track': 'reload'
= javascript_include_tag 'application', 'data-turbo-track': 'reload', defer: true
link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"
script src="https://kit.fontawesome.com/08b1825a15.js" crossorigin="anonymous"
link rel="preconnect" href="https://fonts.googleapis.com"
link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin"
link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet"
script src="https://kit.fontawesome.com/08b1825a15.js" crossorigin="anonymous"
body
== yield
16 changes: 16 additions & 0 deletions app/views/shared/_navbar.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
header.navbar
.container
.navbar-brand
a.navbar-item href="/"
| X-Tracker
span.navbar-burger.burger data-target="navbarMenuHeroA"
span
span
span
.navbar-menu id="navbarMenuHeroA"
.navbar-end
a.navbar-item href="/"
| Home
= link_to "Tweets", tweets_path, class: "navbar-item"
/ a.navbar-item href="/contact"
/ | Contact
32 changes: 32 additions & 0 deletions app/views/tweets/index.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
section.hero.is-info
.hero-head
= render "shared/navbar"
.section
.columns.is-centered
.column.is-1
h1.rotated.has-text-grey Tracked Tweets
.column.is-10
- @tweets.each do |tweet|
.card
.card-content
.media
.media-left
figure class="image is-48x48"
img src="#{tweet.author_avatar_url}" class="is-rounded"
.media-content
p class="title is-4"
= link_to tweet.author_name, tweet.author_url, target: "_blank"
p class="subtitle is-6"
= tweet.body.truncate(280)
.content
= tweet.combined_chart
footer.card-footer
= link_to tweet_path(tweet), class: "card-footer-item primary" do
span View Metrics
span.icon
i class="fa fa-chart-line"
= link_to tweet.url, class: "card-footer-item", target: "_blank" do
span View Tweet
span.icon
i class="fa fa-twitter"
.column.is-1
Loading

0 comments on commit 81a8508

Please sign in to comment.