added 260907 version of worker toolkit
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
class AdditionalPosts
|
||||
protected
|
||||
|
||||
def current_ability
|
||||
@current_ability ||= Ability.new(user)
|
||||
end
|
||||
|
||||
def add_last_new_posts(number, followed_ids)
|
||||
Post.where(_type: "Post")
|
||||
.not_in(_id: followed_ids)
|
||||
.order(created_at: :desc)
|
||||
.to_a
|
||||
.first(number)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,96 @@
|
||||
class ChartListService
|
||||
TRACKABLES = %w[symptom condition treatment].freeze
|
||||
HBI_PAYLOAD = [[1, "Harvey Bradshaw Index".freeze, true].freeze].freeze
|
||||
WEATHERS_PAYLOAD = [
|
||||
[1, "Avg daily humidity".freeze, true].freeze,
|
||||
[2, "Avg daily atmospheric pressure".freeze, true].freeze
|
||||
].freeze
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def initialize(current_user:)
|
||||
@user = current_user
|
||||
end
|
||||
|
||||
def as_json
|
||||
{
|
||||
chart_list: {
|
||||
id: 1,
|
||||
payload: {
|
||||
tags: tags_payload,
|
||||
foods: foods_payload,
|
||||
symptoms: symptoms_payload,
|
||||
conditions: conditions_payload,
|
||||
treatments: treatments_payload,
|
||||
weathersMeasures: weathers_payload,
|
||||
harveyBradshawIndices: hbi_payload
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_ids
|
||||
user.checkins.distinct(:tag_ids)
|
||||
end
|
||||
|
||||
def food_ids
|
||||
user.checkins.distinct(:food_ids)
|
||||
end
|
||||
|
||||
def checkin_ids
|
||||
@_user_checkin_ids ||= user.checkins.distinct(:id)
|
||||
end
|
||||
|
||||
def last_checkin
|
||||
@_last_checkin ||= user.last_checkin
|
||||
end
|
||||
|
||||
def tags_payload
|
||||
Tag::Translation
|
||||
.where(tag_id: tag_ids, locale: I18n.locale)
|
||||
.pluck(:tag_id, :name)
|
||||
.map { |payload| payload << last_checkin.tag_ids.include?(payload.first) }
|
||||
end
|
||||
|
||||
def foods_payload
|
||||
Food::Translation
|
||||
.where(food_id: food_ids, locale: I18n.locale)
|
||||
.pluck(:food_id, :long_desc)
|
||||
.map { |payload| payload << last_checkin.food_ids.include?(payload.first) }
|
||||
end
|
||||
|
||||
def weathers_payload
|
||||
last_checkin&.weather_id ? WEATHERS_PAYLOAD : WEATHERS_PAYLOAD.map { |p| [p[0], p[1], false] }
|
||||
end
|
||||
|
||||
def hbi_payload
|
||||
if Condition::Translation.where(name: "Crohn's disease", id: last_tracked_condition_ids).any?
|
||||
HBI_PAYLOAD
|
||||
else
|
||||
[[HBI_PAYLOAD[0][0], HBI_PAYLOAD[0][1], false]]
|
||||
end
|
||||
end
|
||||
|
||||
TRACKABLES.each do |type|
|
||||
titleized = type.titleize
|
||||
model = "Checkin::#{titleized}".constantize
|
||||
id_field = :"#{type}_id"
|
||||
last_tracked_ids_method = :"last_tracked_#{type}_ids"
|
||||
|
||||
define_method(last_tracked_ids_method) do
|
||||
model.where(checkin_id: last_checkin&.id).distinct(id_field)
|
||||
end
|
||||
|
||||
define_method(:"#{type.pluralize}_payload") do
|
||||
ids = model.where(:checkin_id.in => checkin_ids).distinct(id_field)
|
||||
|
||||
"#{titleized}::Translation"
|
||||
.constantize
|
||||
.where(id_field => ids, :locale => I18n.locale)
|
||||
.pluck(id_field, :name)
|
||||
.map { |payload| payload << send(last_tracked_ids_method).include?(payload.first) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,137 @@
|
||||
class ChartsPattern
|
||||
attr_accessor :start_at, :end_at, :pattern, :user, :checkins_with_weather
|
||||
attr_reader :used_color_ids
|
||||
|
||||
COLOR_IDS = Flaredown::Colorable::IDS
|
||||
ADITIONAL_DAYS = 8
|
||||
|
||||
TYPE_CHART = {
|
||||
line: %w[conditions symptoms],
|
||||
marker: %w[treatments tags foods],
|
||||
hbi: %w[harveyBradshawIndices],
|
||||
weather: %w[weathersMeasures]
|
||||
}.freeze
|
||||
|
||||
SUBTYPE = {
|
||||
static: %i[line marker],
|
||||
dynamic: %i[hbi weather]
|
||||
}.freeze
|
||||
|
||||
def initialize(options)
|
||||
@start_at = options[:start_at]
|
||||
@end_at = options[:end_at]
|
||||
@pattern = options[:pattern]
|
||||
@used_color_ids = []
|
||||
@checkins_with_weather = checkins.where(:weather_id.ne => nil)
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= pattern.author
|
||||
end
|
||||
|
||||
def checkins
|
||||
@checkins ||= user.checkins.order(date: :asc)
|
||||
end
|
||||
|
||||
def scoped_checkins
|
||||
@scoped_checkins ||= checkins.by_date(start_at.to_date - ADITIONAL_DAYS.days, end_at.to_date + ADITIONAL_DAYS.days)
|
||||
end
|
||||
|
||||
def chart_data
|
||||
@data ||=
|
||||
{
|
||||
pattern_id: pattern.id,
|
||||
pattern_name: pattern.name,
|
||||
author_email: user.email,
|
||||
|
||||
series: pattern_includes.map do |chart|
|
||||
category = chart[:category]
|
||||
type = TYPE_CHART.select { |_, value| value.include?(category) }.keys&.first
|
||||
{
|
||||
type: type,
|
||||
subtype: SUBTYPE.select { |_, value| value.include? type }.keys&.first,
|
||||
label: chart[:label],
|
||||
category: category,
|
||||
color_id: get_color_id(chart),
|
||||
data: data(chart)
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def pattern_includes
|
||||
@pattern_includes ||= pattern.includes || []
|
||||
end
|
||||
|
||||
def data(chart)
|
||||
category = chart[:category]
|
||||
id = chart[:id]
|
||||
|
||||
collected_data =
|
||||
if %w[conditions symptoms treatments].include? category
|
||||
static_trackables_coordinates(category, scoped_checkins.map(&:id), id)
|
||||
else
|
||||
health_factors_trackables_coordinate(category, scoped_checkins, id)
|
||||
end
|
||||
|
||||
average = collected_data.inject(0.0) { |result, datum| result + datum[:y].to_f } / collected_data.count
|
||||
|
||||
[{x: collected_data[0]&.fetch(:x), y: average}]
|
||||
end
|
||||
|
||||
def static_trackables_coordinates(category, checkin_ids, id)
|
||||
find_coordinates_by_checkin(category, checkin_ids, id)
|
||||
.sort! { |x, y| x[:x].to_time.to_i <=> y[:x].to_time.to_i }
|
||||
end
|
||||
|
||||
def find_coordinates_by_checkin(category, checkin_ids, id)
|
||||
category_name = category.singularize
|
||||
|
||||
"Checkin::#{category_name.camelize}".constantize.where(
|
||||
checkin_id: {"$in": checkin_ids},
|
||||
"#{category_name}_id": id
|
||||
).map do |tr|
|
||||
coord = {x: tr.checkin.date, y: tr.value}
|
||||
coord[:is_taken] = tr.is_taken if category == "treatments"
|
||||
|
||||
coord
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def health_factors_trackables_coordinate(category, selected_checkins, id)
|
||||
is_weather = %w[weathersMeasures].include? category
|
||||
|
||||
trackables =
|
||||
if %w[foods tags].include? category
|
||||
selected_checkins.select { |checkin| checkin.send("#{category.singularize}_ids").include? id }
|
||||
.map { |checkin| {x: checkin.date} }.uniq
|
||||
elsif is_weather
|
||||
selected_checkins.select(&:weather).map { |checkin| {x: checkin.date, y: checkin.weather.send(id)} }
|
||||
else
|
||||
selected_checkins.select(&:harvey_bradshaw_index)
|
||||
.map { |checkin| {x: checkin.date, y: checkin.harvey_bradshaw_index.score} }
|
||||
end
|
||||
|
||||
trackables.sort! { |x, y| x[:x].to_time.to_i <=> y[:x].to_time.to_i }
|
||||
end
|
||||
|
||||
def get_color_id(chart)
|
||||
model_name = chart[:category].singularize.camelize
|
||||
|
||||
color_id =
|
||||
if Tracking::TYPES.include? model_name
|
||||
user.trackings.where(trackable_type: model_name, trackable_id: chart[:id]).pluck(:color_id).compact.last
|
||||
end
|
||||
|
||||
if color_id.present?
|
||||
used_color_ids << color_id
|
||||
|
||||
return color_id
|
||||
end
|
||||
|
||||
color_id = (COLOR_IDS - used_color_ids).shift
|
||||
used_color_ids << color_id
|
||||
|
||||
color_id
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,72 @@
|
||||
class Checkin::Creator
|
||||
attr_reader :user, :date
|
||||
|
||||
def initialize(user_id, date)
|
||||
@user = User.find(user_id)
|
||||
@date = date
|
||||
end
|
||||
|
||||
# FIXME
|
||||
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
|
||||
def create!
|
||||
prev_checkin = user.last_checkin
|
||||
checkin = Checkin.new(user_id: user.id, date: date, tag_ids: [], food_ids: [])
|
||||
|
||||
if prev_checkin && prev_checkin.position.present?
|
||||
checkin.position_id = prev_checkin.position_id
|
||||
checkin.weather_id = WeatherRetriever.get(date, checkin.position.postal_code)&.id
|
||||
end
|
||||
|
||||
active_trackings = user.trackings.includes(:trackable).active_at(date)
|
||||
condition_attrs = []
|
||||
symptom_attrs = []
|
||||
treatment_attrs = []
|
||||
active_trackings.each do |tracking|
|
||||
trackable = tracking.trackable
|
||||
most_recent_position = user.profile.most_recent_trackable_position_for(trackable)
|
||||
if trackable.is_a? Condition
|
||||
condition_attrs << {
|
||||
condition_id: trackable.id,
|
||||
position: most_recent_position || condition_attrs.length,
|
||||
color_id: tracking.color_id
|
||||
}
|
||||
elsif trackable.is_a? Symptom
|
||||
symptom_attrs << {
|
||||
symptom_id: trackable.id,
|
||||
position: most_recent_position || symptom_attrs.length,
|
||||
color_id: tracking.color_id
|
||||
}
|
||||
elsif trackable.is_a? Treatment
|
||||
treatment_attrs << {
|
||||
treatment_id: trackable.id,
|
||||
position: most_recent_position || treatment_attrs.length,
|
||||
color_id: tracking.color_id,
|
||||
is_taken: false,
|
||||
value: user.profile.most_recent_dose_for(trackable.id)
|
||||
}
|
||||
end
|
||||
end
|
||||
checkin.update!(
|
||||
conditions_attributes: condition_attrs,
|
||||
symptoms_attributes: symptom_attrs,
|
||||
treatments_attributes: treatment_attrs
|
||||
)
|
||||
create_or_update_trackable_usages(checkin)
|
||||
checkin
|
||||
end
|
||||
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/AbcSize
|
||||
|
||||
private
|
||||
|
||||
def create_or_update_trackable_usages(checkin)
|
||||
%w[Condition Symptom Treatment].each do |trackable_class_name|
|
||||
trackable_class = trackable_class_name.constantize
|
||||
trackable_id_method = "#{trackable_class_name.downcase}_id"
|
||||
checkin_trackables_method = trackable_class_name.downcase.pluralize
|
||||
checkin.send(checkin_trackables_method).each do |checkin_trackable|
|
||||
trackable_id = checkin_trackable.send(trackable_id_method)
|
||||
TrackableUsage.create_or_update_by(user: user, trackable: trackable_class.find(trackable_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,193 @@
|
||||
class Checkin::Updater
|
||||
attr_reader :current_user, :checkin, :permitted_params
|
||||
|
||||
def initialize(current_user, params)
|
||||
@current_user = current_user
|
||||
id = params.require(:id)
|
||||
@checkin = Checkin.find(id)
|
||||
@permitted_params =
|
||||
params
|
||||
.require(:checkin)
|
||||
.permit(
|
||||
:note, :postal_code, :weather_id, :promotion_skipped_at,
|
||||
tag_ids: [],
|
||||
food_ids: [],
|
||||
conditions_attributes: [:id, :value, :condition_id, :color_id, :position, :_destroy],
|
||||
symptoms_attributes: [:id, :value, :symptom_id, :color_id, :position, :_destroy],
|
||||
treatments_attributes: [:id, :value, :treatment_id, :is_taken, :color_id, :position, :_destroy]
|
||||
)
|
||||
.tap do |p|
|
||||
set_most_recent_doses(p[:treatments_attributes])
|
||||
update_trackables_positions(p)
|
||||
p[:tag_ids] = [] if p[:tag_ids].nil?
|
||||
p[:food_ids] = [] if p[:food_ids].nil?
|
||||
end
|
||||
end
|
||||
|
||||
def update!
|
||||
checkin.update!(permitted_params.except(:postal_code))
|
||||
|
||||
if checkin.date.today?
|
||||
save_most_recent_doses
|
||||
save_most_recent_trackables_positions
|
||||
end
|
||||
update_trackable_usages
|
||||
|
||||
position = Position.find_or_create_by(postal_code: permitted_params[:postal_code])
|
||||
|
||||
if position.persisted?
|
||||
checkin.position_id = position.id
|
||||
checkin.save!
|
||||
end
|
||||
|
||||
checkin
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_trackables_positions(params)
|
||||
%w[Condition Symptom Treatment].each do |trackable_class_name|
|
||||
update_trackables_positions_on_destroy(trackable_class_name, params)
|
||||
set_added_trackables_positions(trackable_class_name, params)
|
||||
end
|
||||
end
|
||||
|
||||
def update_trackables_positions_on_destroy(trackable_class_name, params)
|
||||
trackables_attrs = trackables_attrs(trackable_class_name, params)
|
||||
removed_trackables = removed_trackables_attrs(trackables_attrs)
|
||||
removed_trackables.each do |removed_trackable|
|
||||
trackables_attrs.each do |trackable|
|
||||
next if trackable[:position].blank? ||
|
||||
removed_trackable[:position].blank? ||
|
||||
trackable[:position] <= removed_trackable[:position]
|
||||
|
||||
trackable[:position] -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_added_trackables_positions(trackable_class_name, params)
|
||||
trackables_attrs = trackables_attrs(trackable_class_name, params)
|
||||
trackable_max = trackables_attrs.reject { |t| t[:position].blank? }.max_by { |t| t[:position] }
|
||||
max_position = trackable_max.present? ? trackable_max[:position] : 0
|
||||
added_trackables = added_trackables_attrs(trackables_attrs)
|
||||
added_trackables.each do |added_trackable|
|
||||
if added_trackable[:position].blank?
|
||||
max_position += 1
|
||||
added_trackable[:position] = max_position
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def save_most_recent_trackables_positions
|
||||
%w[condition symptom treatment].each do |trackable_type|
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
trackables_attributes = permitted_params["#{trackable_type.pluralize}_attributes".to_sym]
|
||||
next if trackables_attributes.blank?
|
||||
trackables_attributes.each do |trackable_attrs|
|
||||
trackable = trackable_class.find(trackable_attrs["#{trackable_type}_id".to_sym])
|
||||
current_user.profile.set_most_recent_trackable_position(
|
||||
trackable, trackable_attrs[:position]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_most_recent_doses(treatments_attrs)
|
||||
return if treatments_attrs.blank?
|
||||
|
||||
added_trackables_attrs(treatments_attrs).each do |t|
|
||||
t[:value] = current_user.profile.most_recent_dose_for(t[:treatment_id])
|
||||
end
|
||||
end
|
||||
|
||||
def save_most_recent_doses
|
||||
treatments_attributes = permitted_params[:treatments_attributes]
|
||||
return if treatments_attributes.blank?
|
||||
treatments_with_doses = treatments_attributes.reject { |t| (t[:value].blank? || t[:is_taken].eql?("false")) }
|
||||
return if treatments_with_doses.empty?
|
||||
treatments_with_doses.each do |t|
|
||||
current_user.profile.set_most_recent_dose(
|
||||
t[:treatment_id], t[:value]
|
||||
)
|
||||
end
|
||||
current_user.profile.save!
|
||||
end
|
||||
|
||||
def update_trackable_usages
|
||||
%w[Condition Symptom Treatment].each do |trackable_class_name|
|
||||
update_removed_trackables_usages(trackable_class_name)
|
||||
update_added_trackables_usages(trackable_class_name)
|
||||
end
|
||||
|
||||
%w[Tag Food].each do |health_class_name|
|
||||
update_health_factors_trackable_usages(health_class_name)
|
||||
end
|
||||
end
|
||||
|
||||
# For trackables that have been removed from checkin, look for TrackableUsage records
|
||||
# for the current user. When a record exists with count 1 it's destroyed,
|
||||
# else count is decremented
|
||||
def update_removed_trackables_usages(trackable_class_name)
|
||||
removed_trackables = removed_trackables_attrs(
|
||||
trackables_attrs(trackable_class_name, permitted_params)
|
||||
)
|
||||
trackable_class = trackable_class_name.constantize
|
||||
trackable_id_key = "#{trackable_class_name.downcase}_id".to_sym
|
||||
removed_trackables.each do |t|
|
||||
trackable_usage = TrackableUsage.find_by(
|
||||
user: current_user,
|
||||
trackable: trackable_class.find(t[trackable_id_key])
|
||||
)
|
||||
|
||||
next if trackable_usage.blank?
|
||||
|
||||
if trackable_usage.count.eql?(1)
|
||||
trackable_usage.destroy
|
||||
else
|
||||
trackable_usage.decrement! :count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def removed_trackables_attrs(trackables_attrs)
|
||||
trackables_attrs.select { |attrs| attrs[:_destroy].eql?("1") }
|
||||
end
|
||||
|
||||
def trackables_attrs(trackable_class_name, params)
|
||||
attrs_key = "#{trackable_class_name.downcase.pluralize}_attributes".to_sym
|
||||
trackables_attrs = params[attrs_key]
|
||||
|
||||
return [] if trackables_attrs.blank?
|
||||
|
||||
trackables_attrs
|
||||
end
|
||||
|
||||
# For trackables that have been added to checkin, look for TrackableUsage records
|
||||
# for the current user. When a record exists its count is incremented,
|
||||
# else a new record is created
|
||||
def update_added_trackables_usages(trackable_class_name)
|
||||
added_trackables = added_trackables_attrs(
|
||||
trackables_attrs(trackable_class_name, permitted_params)
|
||||
)
|
||||
trackable_class = trackable_class_name.constantize
|
||||
trackable_id_key = "#{trackable_class_name.downcase}_id".to_sym
|
||||
added_trackables.each do |t|
|
||||
trackable = trackable_class.find(t[trackable_id_key])
|
||||
TrackableUsage.create_or_update_by(user: current_user, trackable: trackable)
|
||||
end
|
||||
end
|
||||
|
||||
def added_trackables_attrs(trackables_attrs)
|
||||
trackables_attrs.select { |attrs| attrs[:id].blank? }
|
||||
end
|
||||
|
||||
def update_health_factors_trackable_usages(trackable_class_name)
|
||||
trackable_class = trackable_class_name.constantize
|
||||
trackable_key = "#{trackable_class_name.downcase}_ids"
|
||||
|
||||
trackable_class.where(id: permitted_params[trackable_key]).each do |health_factor|
|
||||
TrackableUsage.create_or_update_by(user: current_user, trackable: health_factor)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
class CollectionRetriever
|
||||
attr_reader :occurrences
|
||||
|
||||
def initialize(model, scope, current_user = nil)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail ArgumentError unless [:most_popular, :most_recent].include?(scope)
|
||||
# rubocop:enable Style/SignalException
|
||||
|
||||
@model = model
|
||||
@scope = scope
|
||||
@ids_key = "#{model.name.underscore}_ids"
|
||||
@ids_field = "$#{@ids_key}"
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
def retrieve
|
||||
send @scope
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def most_popular
|
||||
@occurrences = Checkin.collection.aggregate(
|
||||
[
|
||||
# Step 1: have one entry per object id
|
||||
{"$unwind" => @ids_field},
|
||||
# Step 2: group by object id and count occurrences
|
||||
{"$group" => {_id: @ids_field, count: {"$sum" => 1}}},
|
||||
# Step 3: sort by occurrences, descending
|
||||
{"$sort" => {count: -1}},
|
||||
# Step 4: limit to 10 results
|
||||
{"$limit" => 10}
|
||||
]
|
||||
)
|
||||
|
||||
@model.where(id: occurrences.map { |o| o["_id"] }, global: true)
|
||||
end
|
||||
|
||||
def most_recent
|
||||
checkin_relations = Checkin.collection.aggregate(
|
||||
[
|
||||
# Step 1: filter by user
|
||||
{"$match" => {encrypted_user_id: @current_user.encrypted_id}},
|
||||
# Step 2: have one entry per object id
|
||||
{"$unwind" => @ids_field},
|
||||
# Step 3: sort by checkin date, descending
|
||||
{"$sort" => {date: -1}},
|
||||
# Step 4: limit to 10 results
|
||||
{"$limit" => 10}
|
||||
]
|
||||
)
|
||||
|
||||
@model.accessible_by(current_ability).where(id: checkin_relations.map { |o| o[@ids_key] })
|
||||
end
|
||||
|
||||
def current_ability
|
||||
Ability.new(@current_user)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
class DiscussionPosts < AdditionalPosts
|
||||
attr_accessor :params, :user
|
||||
|
||||
TOP_POSTS_DAYS = 7
|
||||
TOP_POSTS_COUNT = 5
|
||||
|
||||
def initialize(params, user)
|
||||
@params = params
|
||||
@user = user
|
||||
end
|
||||
|
||||
def show_list
|
||||
return Post.none unless params
|
||||
|
||||
@posts = Post.accessible_by(current_ability).where(_type: "Post")
|
||||
@posts = @posts.fts(params[:query]) if params[:query].present?
|
||||
@posts = if params[:id].present? && Post::TOPIC_TYPES.include?(params[:type])
|
||||
@posts.where("#{params[:type]}_ids": params[:id].to_i)
|
||||
elsif params[:following].present?
|
||||
@posts.by_followings(user.topic_following)
|
||||
else
|
||||
@posts
|
||||
end
|
||||
end
|
||||
|
||||
def full_top_post_list
|
||||
Post.accessible_by(current_ability)
|
||||
.where(_type: "Post")
|
||||
.by_followings(user.topic_following)
|
||||
.where(:created_at.gte => TOP_POSTS_DAYS.days.ago)
|
||||
.order(comment_reaction_count: :desc)
|
||||
end
|
||||
|
||||
def refined_top_list
|
||||
posts = full_top_post_list.limit(TOP_POSTS_COUNT)
|
||||
|
||||
if Array(posts).size == TOP_POSTS_COUNT
|
||||
posts
|
||||
else
|
||||
posts + add_last_new_posts(TOP_POSTS_COUNT - Array(posts).size, posts.map(&:_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
module Google
|
||||
class RecaptchaVerifier
|
||||
def initialize
|
||||
@secret = ENV["RECAPTCHA_SECRET_KEY"]
|
||||
@url = "https://www.google.com/recaptcha/api/siteverify"
|
||||
end
|
||||
|
||||
def self.exec(response, remoteip = nil)
|
||||
new.invoke_service(response, remoteip)
|
||||
end
|
||||
|
||||
def invoke_service(response, remoteip)
|
||||
params = {
|
||||
secret: @secret,
|
||||
response: response,
|
||||
remoteip: remoteip
|
||||
}
|
||||
response = connection.post @url, params
|
||||
|
||||
return JSON.parse(response.body)["success"] if response.status.eql?(200)
|
||||
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail StandardError, "#{self.class}##{__method__} Got HTTP #{response.status}"
|
||||
# rubocop:enable Style/SignalException
|
||||
end
|
||||
|
||||
def connection
|
||||
Faraday.new(headers: {"Accept" => "application/json"})
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
class PatternCreator
|
||||
attr_accessor :name, :includes, :user, :start_at, :end_at
|
||||
|
||||
def initialize(options)
|
||||
@name = options[:name]
|
||||
@start_at = options[:start_at]
|
||||
@end_at = options[:end_at]
|
||||
|
||||
@includes = options[:includes]
|
||||
@user = User.find_by(id: options[:user_id])
|
||||
end
|
||||
|
||||
def create
|
||||
Pattern.create(
|
||||
name: name,
|
||||
includes: includes,
|
||||
encrypted_user_id: encrypted_user_id
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def encrypted_user_id
|
||||
SymmetricEncryption.encrypt(user.id)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
class SummaryPosts < AdditionalPosts
|
||||
attr_accessor :user, :topic_following, :posts
|
||||
|
||||
SUMMARY_HOURS = 24.hours.ago.strftime("%Y-%m-%d")
|
||||
SUMMARY_POSTS = 3
|
||||
|
||||
def initialize(user)
|
||||
@user = user
|
||||
@topic_following = user.topic_following
|
||||
@posts = Post.accessible_by(current_ability)
|
||||
.where(_type: "Post")
|
||||
.by_followings(@topic_following)
|
||||
.where(:created_at.gte => SUMMARY_HOURS)
|
||||
end
|
||||
|
||||
def show_list
|
||||
posts = list.first(SUMMARY_POSTS)
|
||||
(posts.count == SUMMARY_POSTS) ? posts : posts + add_last_new_posts(SUMMARY_POSTS - posts.count, posts.map(&:_id))
|
||||
end
|
||||
|
||||
def list
|
||||
topic_frequency.each_with_object([]) do |obj, array|
|
||||
array << Post.where(_type: "Post").find(obj[0])
|
||||
array.last.extend(PostExtender).frequency_topic_priority(topic_frequency[obj[0]])
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def topic_frequency
|
||||
posts.frequency_by(topic_following).sort_by { |_k, v| v }.reverse.to_h
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
class TrackableCreator
|
||||
# Assumes trackable initialized by CanCanCan
|
||||
def initialize(trackable, user)
|
||||
@trackable = trackable
|
||||
@user = user
|
||||
end
|
||||
|
||||
def create!
|
||||
searchable_attr = @trackable.instance_of?(Food) ? "long_desc" : "name"
|
||||
same_trackable = @trackable
|
||||
.class
|
||||
.with_translations(I18n.locale)
|
||||
.where("#{searchable_attr} ILIKE :name", name: @trackable.name)
|
||||
.first
|
||||
|
||||
@trackable = same_trackable if same_trackable.present?
|
||||
unless @trackable.global? || user_trackable_exists?
|
||||
user_trackable_class.create!(
|
||||
:user => @user, trackable_attr.to_sym => @trackable
|
||||
)
|
||||
@trackable.reload
|
||||
end
|
||||
@trackable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_trackable_class
|
||||
"User#{@trackable.class.name}".constantize
|
||||
end
|
||||
|
||||
def trackable_attr
|
||||
@trackable.class.name.underscore
|
||||
end
|
||||
|
||||
def user_trackable_exists?
|
||||
return false if @trackable.id.nil?
|
||||
user_trackable_class.find_by(
|
||||
:user_id => @user.id, "#{trackable_attr}_id".to_sym => @trackable.id
|
||||
).present?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
class TrackingDestroyer
|
||||
attr_reader :user, :tracking, :date
|
||||
|
||||
def initialize(user, tracking, date)
|
||||
@user = user
|
||||
@tracking = tracking
|
||||
@date = date
|
||||
end
|
||||
|
||||
def destroy
|
||||
# If a tracking for the same trackable started at the same date is found,
|
||||
# then it can be destroyed because we're in the case of an unwanted action
|
||||
same_tracking = user.trackings.find_by(
|
||||
trackable_id: tracking.trackable_id,
|
||||
trackable_type: tracking.trackable_type,
|
||||
start_at: date
|
||||
)
|
||||
if same_tracking.present?
|
||||
tracking.destroy
|
||||
else
|
||||
tracking.update!(end_at: Time.zone.today)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
class WeatherRetriever
|
||||
class << self
|
||||
def get(date, postal_code)
|
||||
position = Position.find_or_create_by(postal_code: postal_code)
|
||||
|
||||
weather = Weather.find_by(date: date, position_id: position&.id)
|
||||
|
||||
return weather if weather.present?
|
||||
|
||||
if position&.latitude.blank? || position&.longitude.blank?
|
||||
Rails.logger.warn "No coordinates found for postal_code #{postal_code}: #{position.inspect}"
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
forecast = get_forecast(date, position)
|
||||
|
||||
if forecast.status != 200
|
||||
Rails.logger.warn "No forecast found for position #{position.inspect}: response code was #{forecast.status}, headers were #{forecast.headers}, body contained #{forecast.body}"
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
create_weather(forecast, position.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_forecast(date, position)
|
||||
Tomorrowiorb.forecast(
|
||||
"#{position.latitude},#{position.longitude}",
|
||||
["1d"],
|
||||
"imperial"
|
||||
)
|
||||
end
|
||||
|
||||
def create_weather(forecast, position_id)
|
||||
today = JSON.parse(forecast.body, symbolize_names: true).dig(:timelines, :daily, 0)
|
||||
the_time = today.dig(:time)
|
||||
today = today.dig(:values)
|
||||
rain_intensity = today.dig(:rainIntensityAvg)
|
||||
sleet_intensity = today.dig(:sleetIntensityAvg)
|
||||
snow_intensity = today.dig(:snowIntensityAvg)
|
||||
icon = get_icon_legacy(today)
|
||||
summary = "General conditions are #{icon}, with an average temperature of #{today[:temperatureAvg]}."
|
||||
Weather.create(
|
||||
date: Date.strptime(the_time, "%Y-%m-%d"),
|
||||
humidity: today.dig(:humidityAvg).round,
|
||||
icon: icon,
|
||||
position_id: position_id,
|
||||
precip_intensity: rain_intensity + sleet_intensity + snow_intensity,
|
||||
pressure: today.dig(:pressureSurfaceLevelAvg),
|
||||
summary: summary,
|
||||
temperature_max: today.dig(:temperatureMax),
|
||||
temperature_min: today.dig(:temperatureMin)
|
||||
)
|
||||
end
|
||||
|
||||
def get_icon_legacy(today)
|
||||
# Our icons do not coverage their full range of weather codes. We could pull in their icons (linked below) on the frontend to expand options
|
||||
# This method adapts their weather codes to our existing icons as best as possible
|
||||
# Icons and codes found here: https://docs.tomorrow.io/reference/data-layers-weather-codes
|
||||
# Icon files here: https://github.com/Tomorrow-IO-API/tomorrow-weather-codes
|
||||
# Daily forecast is always daytime weather codes / icons regardless of actual time
|
||||
code = if today["weatherCodeMin"]
|
||||
today["weatherCodeMin"]
|
||||
elsif today["weatherCodeFullDay"]
|
||||
today["weatherCodeFullDay"]
|
||||
else
|
||||
today["weatherCode"]
|
||||
end
|
||||
|
||||
case code
|
||||
when 1000, 1100
|
||||
"clear-day"
|
||||
when 1101
|
||||
"partly-cloudy-day"
|
||||
when 1102, 1001, 8000
|
||||
"cloudy"
|
||||
when 2000, 2100
|
||||
"fog"
|
||||
when 4000, 4001, 4200, 4201
|
||||
"rain"
|
||||
when 5000, 5001, 5100, 5101
|
||||
"snow"
|
||||
when 6000, 6001, 6200, 6201, 7000, 7101, 7102
|
||||
"sleet"
|
||||
else
|
||||
"default"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user