Last active
March 5, 2022 11:27
-
-
Save jmarsh24/ff913b9083086648c08d59aa26ce2f9f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Video < ApplicationRecord | |
acts_as_votable | |
include Filterable | |
PERFORMANCE_REGEX=/(?<=\s|^|#)[1-8]\s?(of|de|\/|-)\s?[1-8](\s+$|)/.freeze | |
validates :youtube_id, presence: true, uniqueness: true | |
belongs_to :leader, optional: true, counter_cache: true | |
belongs_to :follower, optional: true, counter_cache: true | |
belongs_to :song, optional: true, counter_cache: true | |
belongs_to :channel, optional: false, counter_cache: true | |
belongs_to :event, optional: true, counter_cache: true | |
scope :filter_by_orchestra, ->(song_artist, _user) { joins(:song).where("unaccent(songs.artist) ILIKE unaccent(?)", song_artist)} | |
scope :filter_by_genre, ->(song_genre, _user) { joins(:song).where("unaccent(songs.genre) ILIKE unaccent(?)", song_genre) } | |
scope :filter_by_leader, ->(leader, _user) { joins(:leader).where("unaccent(leaders.name) ILIKE unaccent(?)", leader) } | |
scope :filter_by_follower, ->(follower, _user) { joins(:follower).where("unaccent(followers.name) ILIKE unaccent(?)", follower) } | |
scope :filter_by_channel, ->(channel_id, _user) { joins(:channel).where("channels.channel_id ILIKE ?", channel_id) } | |
scope :filter_by_event_id, ->(event_id, _user) { where(event_id: event_id) } | |
scope :filter_by_song_id, ->(song_id, _user) { where(song_id: song_id) } | |
scope :filter_by_hd, ->(boolean, _user) { where(hd: boolean) } | |
scope :filter_by_year,->(year, _user) { where("extract(year from performance_date) = ?", year) } | |
scope :filter_by_upload_year,->(year, _user) { where("extract(year from upload_date) = ?", year) } | |
scope :hidden, -> { where(hidden: true) } | |
scope :not_hidden, -> { where(hidden: false) } | |
scope :paginate, ->(page, per_page) { offset(per_page * (page - 1)).limit(per_page) } | |
# Active Admin scopes | |
scope :has_song, -> { where.not(song_id: nil) } | |
scope :has_leader, -> { where.not(leader_id: nil) } | |
scope :has_follower, -> { where.not(follower_id: nil) } | |
scope :missing_follower, -> { where(follower_id: nil) } | |
scope :missing_leader, -> { where(leader_id: nil) } | |
scope :missing_song, -> { where(song_id: nil) } | |
# Youtube Music Scopes | |
scope :scanned_youtube_music, -> { where(scanned_youtube_music: true) } | |
scope :not_scanned_youtube_music, -> { where(scanned_youtube_music: false) } | |
scope :has_youtube_song, -> { where.not(youtube_song: nil) } | |
# AcrCloud Response scopes | |
scope :successful_acrcloud, -> { where(acr_response_code: 0) } | |
scope :not_successful_acrcloud, -> { where(acr_response_code: 1001) } | |
scope :scanned_acrcloud, -> { where(acr_response_code: [0, 1001]) } | |
scope :not_scanned_acrcloud, | |
-> { | |
where | |
.not(acr_response_code: [0, 1001]) | |
.or(Video.where(acr_response_code: nil)) | |
} | |
# Attribute Matching Scopes | |
scope :with_song_title, | |
lambda { |song_title| | |
where( | |
'unaccent(spotify_track_name) ILIKE unaccent(:song_title) | |
OR unaccent(youtube_song) ILIKE unaccent(:song_title) | |
OR unaccent(title) ILIKE unaccent(:song_title) | |
OR unaccent(description) ILIKE unaccent(:song_title) | |
OR unaccent(tags) ILIKE unaccent(:song_title) | |
OR unaccent(acr_cloud_track_name) ILIKE unaccent(:song_title)', | |
song_title: "%#{song_title}%" | |
) | |
} | |
scope :with_song_artist_keyword, | |
lambda { |song_artist_keyword| | |
where( | |
'spotify_artist_name ILIKE :song_artist_keyword | |
OR unaccent(spotify_artist_name_2) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(youtube_artist) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(description) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(title) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(tags) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(spotify_album_name) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(acr_cloud_album_name) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(acr_cloud_artist_name) ILIKE unaccent(:song_artist_keyword) | |
OR unaccent(acr_cloud_artist_name_1) ILIKE unaccent(:song_artist_keyword)', | |
song_artist_keyword: "%#{song_artist_keyword}%" | |
) | |
} | |
scope :with_dancer_name_in_title, | |
lambda { |dancer_name| | |
where( | |
"unaccent(title) ILIKE unaccent(:dancer_name)", | |
dancer_name: "%#{dancer_name}%" | |
) | |
} | |
# Combined Scopes | |
scope :title_match_missing_leader, | |
->(leader_name) { | |
missing_leader.with_dancer_name_in_title(leader_name) | |
} | |
scope :title_match_missing_follower, | |
->(follower_name) { | |
missing_follower.with_dancer_name_in_title(follower_name) | |
} | |
class << self | |
def filter_by_watched(boolean, user) | |
case boolean | |
when "true" | |
where(id: Ahoy::Event.viewed_by_user(user)) | |
when "false" | |
where.not(id: Ahoy::Event.viewed_by_user(user)) | |
end | |
end | |
def filter_by_liked(boolean, user) | |
case boolean | |
when "true" | |
where(id: user.find_up_voted_items.map(&:id)) | |
when "false" | |
where(id: user.find_up_downsvoted_items.map(&:id)) | |
end | |
end | |
# Filters videos by the results from the materialized | |
# full text search out of from VideosSearch | |
def filter_by_query(query, _user) | |
where(id: VideosSearch.search(query_without_stop_words(query)).select(:video_id)) | |
end | |
def stop_words | |
%w[and or the a an of to y e &] | |
end | |
def stop_words_regex | |
/\b(#{stop_words.map { |word| Regexp.escape(word) }.join('|')})\b/ | |
end | |
def query_without_stop_words(query) | |
query.gsub(stop_words_regex, "").gsub("'", "").split.map(&:strip).join(" ") | |
end | |
def most_viewed_videos_by_month | |
where( id: Ahoy::Event.most_viewed_videos_by_month) | |
end | |
end | |
def grep_title_leader_follower | |
self.leader = Leader.all.find { |leader| title.parameterize.match(leader.name.parameterize) } | |
self.follower = Follower.all.find { |follower| title.parameterize.match(follower.name.parameterize) } | |
save | |
end | |
def grep_performance_number | |
array = Array.new | |
array << title if title.present? | |
array << description if description.present? | |
search_string = array.join(" ") | |
return unless search_string.match?(PERFORMANCE_REGEX) | |
performance_array = search_string.match(PERFORMANCE_REGEX)[0].tr("^0-9", " ").split(" ").map(&:to_i) | |
return if performance_array.empty? | |
return if performance_array.first > performance_array.second || performance_array.second == 1 | |
self.performance_number = performance_array.first | |
self.performance_total_number = performance_array.second | |
save | |
end | |
def grep_title_description_acr_cloud_song | |
array = Array.new | |
array << title if title.present? | |
array << description if description.present? | |
array << spotify_artist_name if spotify_artist_name.present? | |
array << spotify_artist_name_2 if spotify_artist_name_2.present? | |
array << spotify_track_name if spotify_track_name.present? | |
array << spotify_album_name if spotify_album_name.present? | |
array << acr_cloud_artist_name if acr_cloud_artist_name.present? | |
array << acr_cloud_artist_name_1 if acr_cloud_artist_name_1.present? | |
array << acr_cloud_track_name if acr_cloud_track_name.present? | |
array << acr_cloud_album_name if acr_cloud_album_name.present? | |
array << youtube_song if youtube_song.present? | |
array << youtube_artist if youtube_artist.present? | |
search_string = array.join(" ") | |
self.song = Song.filter_by_active | |
.find_all { |song| search_string.parameterize.match(song.title.parameterize)} | |
.find { |song| search_string.parameterize.match(song.last_name_search.parameterize)} | |
save | |
end | |
def display | |
@display ||= Video::Display.new(self) | |
end | |
def clicked! | |
increment(:click_count) | |
increment(:popularity) | |
save! | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class VideosController < ApplicationController | |
include ActionView::RecordIdentifier | |
before_action :authenticate_user!, only: %i[edit update] | |
before_action :current_search, only: %i[index] | |
before_action :set_video, only: %i[update edit] | |
before_action :set_recommended_videos, only: %i[edit] | |
helper_method :sorting_params, :filtering_params | |
def index | |
@page = page | |
@search = | |
Video::Search.for( | |
filtering_params: filtering_params, | |
sorting_params: sorting_params, | |
page: page, | |
user: current_user | |
) | |
if (sorting_params.empty? && page == 1 && @search.videos.size > 20 && filtering_for_dancer?) || dancer_name_match? | |
@search_most_recent = | |
Video::Search.for( | |
filtering_params: filtering_params, | |
sorting_params: { direction: "desc", sort: "videos.performance_date" }, | |
page: page, | |
user: current_user | |
) | |
@search_oldest = | |
Video::Search.for( | |
filtering_params: filtering_params, | |
sorting_params: { direction: "asc", sort: "videos.upload_date" }, | |
page: page, | |
user: current_user | |
) | |
@search_most_popular = | |
Video::Search.for( | |
filtering_params: filtering_params, | |
sorting_params: { direction: "desc", sort: "videos.popularity" }, | |
page: page, | |
user: current_user | |
) | |
@search_most_popular_new_to_you = | |
Video::Search.for( | |
filtering_params: filtering_params.merge(watched: "false"), | |
sorting_params: { direction: "desc", sort: "videos.popularity" }, | |
page: page, | |
user: current_user | |
) | |
@search_most_popular_watched = | |
Video::Search.for( | |
filtering_params: filtering_params.merge(watched: "true"), | |
sorting_params: { direction: "desc", sort: "videos.popularity" }, | |
page: page, | |
user: current_user | |
) | |
end | |
end | |
def edit; end | |
def show | |
@video = Video.find_by(youtube_id: show_params[:v]) | |
@start_value = params[:start] | |
@end_value = params[:end] | |
@root_url = root_url | |
@playback_speed = params[:speed] || "1" | |
set_recommended_videos | |
@video.clicked! | |
UpdateVideoWorker.perform_async(@video.youtube_id) | |
ahoy.track("Video View", video_id: Video.find_by(youtube_id: show_params[:v]).id ) | |
end | |
def update | |
@video.update(video_params) | |
redirect_to watch_path(v: @video.youtube_id) | |
end | |
def create | |
@video = Video.create(youtube_id: params[:video][:youtube_id]) | |
fetch_new_video | |
redirect_to root_path, | |
notice: | |
"Video Sucessfully Added: The video must be approved before the videos are added" | |
end | |
def upvote | |
@video = Video.find(params[:id]) | |
if current_user.voted_up_on? @video | |
@video.unvote_by current_user | |
else | |
@video.upvote_by current_user | |
end | |
render turbo_stream: turbo_stream.update("#{dom_id(@video)}_vote", partial: "videos/show/vote") | |
end | |
def downvote | |
@video = Video.find(params[:id]) | |
if current_user.voted_down_on? @video | |
@video.unvote_by current_user | |
else | |
@video.downvote_by current_user | |
end | |
render turbo_stream: turbo_stream.update("#{dom_id(@video)}_vote", partial: "videos/show/vote") | |
end | |
private | |
def set_video | |
@video = Video.find(params[:id]) | |
end | |
def set_recommended_videos | |
videos_from_this_performance | |
videos_with_same_event | |
videos_with_same_song | |
videos_with_same_channel | |
end | |
def videos_from_this_performance | |
@videos_from_this_performance = Video.where("upload_date <= ?", @video.upload_date + 7.days) | |
.where("upload_date >= ?", @video.upload_date - 7.days) | |
.where(channel_id: @video.channel_id) | |
.where(leader_id: @video.leader_id) | |
.where(follower_id: @video.follower_id) | |
.order("performance_number ASC") | |
.where(hidden: false) | |
.where | |
.not(youtube_id: @video.youtube_id) | |
.limit(8).load_async | |
end | |
def videos_with_same_event | |
@videos_with_same_event = Video.where(event_id: @video.event_id) | |
.where.not(event: nil) | |
.where("upload_date <= ?", @video.upload_date + 7.days) | |
.where("upload_date >= ?", @video.upload_date - 7.days) | |
.where(hidden: false) | |
.where.not(youtube_id: @video.youtube_id) | |
.limit(8).load_async | |
@videos_with_same_event -= @videos_from_this_performance | |
end | |
def videos_with_same_song | |
@videos_with_same_song = Video.where(song_id: @video.song_id) | |
.has_leader.has_follower | |
.where(hidden: false) | |
.where.not(song_id: nil) | |
.where.not(youtube_id: @video.youtube_id) | |
.limit(8).load_async | |
end | |
def videos_with_same_channel | |
@videos_with_same_channel = Video.where(channel_id: @video.channel_id) | |
.has_leader.has_follower | |
.where(hidden: false) | |
.where.not(youtube_id: @video.youtube_id) | |
.limit(8).load_async | |
end | |
def current_search | |
@current_search = params[:query] | |
end | |
def page | |
@page ||= params.permit(:page).fetch(:page, 1).to_i | |
end | |
def video_params | |
params | |
.require(:video) | |
.permit(:leader_id, | |
:follower_id, | |
:song_id, | |
:event_id, | |
:hidden, | |
:"performance_date(1i)", | |
:"performance_date(2i)", | |
:"performance_date(3i)", | |
:performance_number, | |
:performance_total_number, | |
:id) | |
end | |
def filtering_params | |
params.permit( | |
:leader, | |
:follower, | |
:channel, | |
:genre, | |
:orchestra, | |
:song_id, | |
:query, | |
:hd, | |
:event_id, | |
:year, | |
:watched, | |
:liked | |
) | |
end | |
def sorting_params | |
params.permit(:direction, :sort) | |
end | |
def show_params | |
params.permit(:v) | |
end | |
def filtering_for_dancer? | |
return true if filtering_params.include?(:leader) || filtering_params.include?(:follower) | |
end | |
def dancer_name_match? | |
if filtering_params.fetch(:query, false).present? | |
Leader.full_name_search(filtering_params.fetch(:query, false)) || Follower.full_name_search(filtering_params.fetch(:query, false)) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Video::Search | |
SEARCHABLE_COLUMNS = %w[ | |
songs.title | |
songs.last_name_search | |
videos.channel_id | |
videos.performance_date | |
videos.view_count | |
videos.updated_at | |
videos.popularity | |
videos.like_count | |
].freeze | |
NUMBER_OF_VIDEOS_PER_PAGE = 60 | |
NUMBER_OF_VIDEOS_PER_ROW = 5 | |
class << self | |
def for(filtering_params:, sorting_params:, page:, user:) | |
new( | |
filtering_params: filtering_params, | |
sorting_params: sorting_params, | |
page: page, | |
user: user | |
) | |
end | |
end | |
def initialize(filtering_params: {}, sorting_params: {}, page: 1, user: nil) | |
@filtering_params = filtering_params | |
@sorting_params = sorting_params | |
@page = page | |
@user = user | |
end | |
def videos | |
@videos = | |
Video | |
.not_hidden | |
.includes(:leader, :follower, :channel, :song, :event) | |
.order(ordering_params) | |
.filter_videos(@filtering_params, @user) | |
return @videos unless @filtering_params.empty? && @sorting_params.empty? | |
@videos.most_viewed_videos_by_month | |
.has_leader | |
.has_follower | |
end | |
def paginated_videos | |
@paginated_videos = videos.paginate(@page, NUMBER_OF_VIDEOS_PER_PAGE).load_async | |
end | |
def paginated_row | |
@paginated_videos_row = videos.paginate(@page, NUMBER_OF_VIDEOS_PER_ROW).load_async | |
end | |
def displayed_videos_count | |
@displayed_videos_count ||= ((@page - 1) * NUMBER_OF_VIDEOS_PER_PAGE) + paginated_videos.size | |
end | |
def next_page? | |
@next_page ||= displayed_videos_count < videos.size | |
end | |
def leaders | |
@leaders ||= facet("leaders.name", :leader) | |
end | |
def followers | |
@followers ||= facet("followers.name", :follower) | |
end | |
def orchestras | |
@orchestras ||= facet("songs.artist", :song) | |
end | |
def genres | |
@genres ||= facet("songs.genre", :song) | |
end | |
def years | |
@years ||= facet_on_year("performance_date") | |
end | |
def songs | |
@songs ||= facet("songs.title", :song) | |
end | |
def sort_column | |
SEARCHABLE_COLUMNS.include?(@sorting_params[:sort]) ? @sorting_params[:sort] : "videos.popularity" | |
end | |
def sort_direction | |
%w[asc desc].include?(@sorting_params[:direction]) ? @sorting_params[:direction] : "desc" | |
end | |
def filter_column | |
@filtering_params | |
end | |
private | |
def ordering_params | |
@filtering_params.empty? && @sorting_params.empty? ? "RANDOM()": "#{sort_column} #{sort_direction}" | |
end | |
def facet_on_year(table_column) | |
query = | |
"extract(year from #{table_column})::int AS facet_value, count(#{table_column}) AS occurrences" | |
counts = | |
Video | |
.filter_videos(@filtering_params, @user) | |
.not_hidden | |
.select(query) | |
.group("facet_value") | |
.order("facet_value DESC") | |
.having("count(#{table_column}) > 0") | |
.load_async | |
counts.map { |c| ["#{c.facet_value} (#{c.occurrences})", c.facet_value] } | |
end | |
def facet(table_column, model) | |
query = | |
"#{table_column} AS facet_value, count(#{table_column}) AS occurrences" | |
counts = | |
Video | |
.filter_videos(@filtering_params, @user) | |
.not_hidden | |
.joins(model) | |
.select(query) | |
.group(table_column) | |
.order("occurrences DESC") | |
.having("count(#{table_column}) > 0") | |
.load_async | |
counts.map do |c| | |
["#{c.facet_value.split("'").map(&:titleize).join("'")} (#{c.occurrences})", c.facet_value.downcase] | |
end | |
end | |
def facet_id(table_column, table_column_id, model) | |
query = | |
"#{table_column} AS facet_value, count(#{table_column}) AS occurrences, #{table_column_id} AS facet_id_value" | |
counts = | |
Video | |
.filter_videos(@filtering_params, @user) | |
.not_hidden | |
.joins(model) | |
.select(query) | |
.group(table_column, table_column_id) | |
.order("occurrences DESC") | |
.having("count(#{table_column}) > 0") | |
.load_async | |
counts.map do |c| | |
["#{c.facet_value.titleize} (#{c.occurrences})", c.facet_id_value] | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment