added 260907 version of worker toolkit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
//= link_tree ../images
|
||||
//= link_directory ../javascripts .js
|
||||
//= link_directory ../stylesheets .css
|
||||
@@ -0,0 +1,28 @@
|
||||
module Api
|
||||
module V1
|
||||
class AwsSesController < ApplicationController
|
||||
skip_authorize_resource only: [:mail_it, :notification]
|
||||
skip_before_action :authenticate_user!, only: [:mail_it, :notification]
|
||||
|
||||
def notification
|
||||
message_type = request.headers["x-amz-sns-message-type"]
|
||||
# sns_topic = request.headers['x-amz-sns-topic-arn']
|
||||
raw_post = request.raw_post
|
||||
|
||||
if message_type.include? "Confirmation"
|
||||
send_subscription_confirmation(raw_post)
|
||||
elsif message_type.include? "Notification"
|
||||
EmailRejectDispatcher.perform_async(raw_post)
|
||||
end
|
||||
|
||||
render nothing: true, status: 200
|
||||
end
|
||||
|
||||
def send_subscription_confirmation(raw_post)
|
||||
json = JSON.parse(raw_post)
|
||||
|
||||
open(json["SubscribeURL"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class ChartListsController < ApplicationController
|
||||
def show
|
||||
render json: ChartListService.new(current_user: current_user).as_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
module Api
|
||||
module V1
|
||||
class ChartsController < ApplicationController
|
||||
def show
|
||||
chart = Chart.new(chart_params)
|
||||
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActiveRecord::RecordInvalid, chart) if chart.invalid?
|
||||
# rubocop:enable Style/SignalException
|
||||
|
||||
render json: chart
|
||||
end
|
||||
|
||||
def chart_params
|
||||
includes_params = {
|
||||
tags: [],
|
||||
foods: [],
|
||||
symptoms: [],
|
||||
conditions: [],
|
||||
treatments: [],
|
||||
weathersMeasures: [],
|
||||
harveyBradshawIndices: []
|
||||
}
|
||||
|
||||
params.permit(:id, :start_at, :end_at, includes: includes_params).tap do |whitelist|
|
||||
whitelist[:user] = current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
module Api
|
||||
module V1
|
||||
class ChartsPatternController < ApplicationController
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
|
||||
def index
|
||||
offset = charts_pattern_params[:offset].to_i
|
||||
start_at = (charts_pattern_params[:start_at].to_date - offset.days).to_s
|
||||
|
||||
end_date = charts_pattern_params[:end_at].to_date
|
||||
end_at = ((Time.current.to_date == end_date) ? end_date : (end_date + offset.days)).to_s
|
||||
|
||||
@patterns = Pattern.where(id: {"$in": charts_pattern_params[:pattern_ids] || []})
|
||||
|
||||
@extended_patterns = @patterns.map do |pattern|
|
||||
pattern.extend(PatternExtender).form_chart_data(start_at: start_at,
|
||||
end_at: end_at,
|
||||
pattern: pattern)
|
||||
end
|
||||
|
||||
render json: @extended_patterns, meta: {color_ids: Flaredown::Colorable::IDS}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def charts_pattern_params
|
||||
params.permit(:start_at, :end_at, :offset, pattern_ids: [])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
module Api
|
||||
module V1
|
||||
class CheckinsController < ApplicationController
|
||||
def index
|
||||
date = params[:date]
|
||||
|
||||
if date.blank? && params.require(:page)
|
||||
render json: current_user.checkins.where(:note.nin => [nil, ""]).order_by(date: :desc).page(params[:page]).per(10)
|
||||
else
|
||||
render json: current_user.checkins.includes([:harvey_bradshaw_index, :promotion_rate, :conditions, :symptoms, :treatments]).select { |x|
|
||||
x.date.to_date == Date.parse(date)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
render json: Checkin.find(id)
|
||||
end
|
||||
|
||||
def create
|
||||
date = params.require(:checkin).require(:date)
|
||||
parsed = DateTime.parse(date)
|
||||
now = DateTime.current
|
||||
save_date = DateTime.new(parsed.year, parsed.month, parsed.day, now.hour, now.minute, now.second)
|
||||
checkin = Checkin::Creator.new(current_user.id, save_date).create!
|
||||
render json: checkin
|
||||
end
|
||||
|
||||
def update
|
||||
render json: Checkin::Updater.new(current_user, params).update!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def id
|
||||
params.require(:id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
module Api
|
||||
module V1
|
||||
class CommentsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
|
||||
def index
|
||||
render json: @comments.where(:id.in => params[:ids]).order_by(created_at: :asc)
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @comment
|
||||
end
|
||||
|
||||
def create
|
||||
@comment.encrypted_user_id = current_user.encrypted_id
|
||||
|
||||
if @comment.save
|
||||
UpdatePostCountersJob.perform_async(parent_id: create_params[:post_id], parent_type: "Post")
|
||||
|
||||
unless @comment.encrypted_user_id == @comment.post.encrypted_user_id
|
||||
Notification.create(
|
||||
kind: :comment,
|
||||
notificateable: @comment,
|
||||
encrypted_user_id: @comment.encrypted_user_id,
|
||||
encrypted_notify_user_id: @comment.post.encrypted_user_id
|
||||
)
|
||||
end
|
||||
|
||||
DiscussionMention.perform_async(current_user.encrypted_id, @comment.id.to_s)
|
||||
|
||||
render json: @comment, status: :created
|
||||
else
|
||||
render json: {errors: @comment.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:comment).permit(:body, :post_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
module Api
|
||||
module V1
|
||||
class ConditionsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:show]
|
||||
|
||||
def index
|
||||
@conditions = @conditions.includes(:translations)
|
||||
@conditions = ids.present? ? @conditions.where(id: ids) : @conditions.order(:name).limit(50)
|
||||
|
||||
render json: @conditions
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @condition
|
||||
end
|
||||
|
||||
def create
|
||||
render json: TrackableCreator.new(@condition, current_user).create!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:condition).permit(:name)
|
||||
end
|
||||
|
||||
def ids
|
||||
@ids ||= params[:ids] if params[:ids].is_a?(Array)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
module Api
|
||||
module V1
|
||||
class CountriesController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render json: Country.all, each_serializer: CountrySerializer
|
||||
end
|
||||
|
||||
def show
|
||||
country = Country.find_country_by_alpha2(alpha2)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail ActiveRecord::RecordNotFound if country.nil?
|
||||
# rubocop:enable Style/SignalException
|
||||
render json: country, serializer: CountrySerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def alpha2
|
||||
id = params.require(:id)
|
||||
match_data = /^[[:alpha:]]{2}$/.match(id)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActionController::BadRequest, "id param must be a 2 alphabetic characters string") if match_data.nil?
|
||||
# rubocop:enable Style/SignalException
|
||||
match_data[0]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
module Api
|
||||
module V1
|
||||
class DataExportSchedulesController < ApplicationController
|
||||
def create
|
||||
DataExportJob.perform_later(current_user.id)
|
||||
|
||||
head :created
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
module Api
|
||||
module V1
|
||||
class DayHabitsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render json: DayHabit.all
|
||||
end
|
||||
|
||||
def show
|
||||
day_habit = DayHabit.find(day_habit_id)
|
||||
render json: day_habit
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def day_habit_id
|
||||
id = params.require(:id)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActionController::BadRequest, "id param is not a valid day_habit id") unless DayHabit.all_ids.include?(id)
|
||||
# rubocop:enable Style/SignalException
|
||||
id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Api
|
||||
module V1
|
||||
class DiscoursesController < ApplicationController
|
||||
def create
|
||||
render json: {url: DiscourseClient.new(current_user, params).generate_url}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
module Api
|
||||
module V1
|
||||
class EducationLevelsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render json: EducationLevel.all
|
||||
end
|
||||
|
||||
def show
|
||||
education_level = EducationLevel.find(education_level_id)
|
||||
render json: education_level
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def education_level_id
|
||||
id = params.require(:id)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
unless EducationLevel.all_ids.include?(id)
|
||||
fail(ActionController::BadRequest, "id param is not a valid education_level id")
|
||||
end
|
||||
# rubocop:enable Style/SignalException
|
||||
id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
module Api
|
||||
module V1
|
||||
class EthnicitiesController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render json: Ethnicity.all
|
||||
end
|
||||
|
||||
def show
|
||||
ethnicity = Ethnicity.find(ethnicity_id)
|
||||
render json: ethnicity
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ethnicity_id
|
||||
id = params.require(:id)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActionController::BadRequest, "id param is not a valid ethnicity id") unless Ethnicity.all_ids.include?(id)
|
||||
# rubocop:enable Style/SignalException
|
||||
id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
module Api
|
||||
module V1
|
||||
class FoodsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def index
|
||||
@foods = @foods.includes(:translations)
|
||||
|
||||
foods =
|
||||
if ids.present?
|
||||
@foods.where(id: ids)
|
||||
elsif scope.present?
|
||||
CollectionRetriever.new(Food, scope, current_user).retrieve
|
||||
end
|
||||
|
||||
render json: foods
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @food
|
||||
end
|
||||
|
||||
def create
|
||||
render json: TrackableCreator.new(@food, current_user).create!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
{long_desc: params.require(:food).require(:name)}
|
||||
end
|
||||
|
||||
def ids
|
||||
@ids ||= params[:ids]
|
||||
end
|
||||
|
||||
def scope
|
||||
@scope ||= params[:scope]&.to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
module Api
|
||||
module V1
|
||||
class HarveyBradshawIndicesController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def show
|
||||
render json: @harvey_bradshaw_index
|
||||
end
|
||||
|
||||
def create
|
||||
@harvey_bradshaw_index.save
|
||||
|
||||
render json: @harvey_bradshaw_index
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:harvey_bradshaw_index).permit(
|
||||
:abdominal_mass, :abdominal_pain, :abscess,
|
||||
:anal_fissure, :aphthous_ulcers, :arthralgia,
|
||||
:checkin_id, :erythema_nodosum, :new_fistula,
|
||||
:pyoderma_gangrenosum, :stools, :uveitis, :well_being
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
module Api
|
||||
module V1
|
||||
class InvitationsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def show
|
||||
render json: Invitation.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
invitation = Invitation.find(params[:id])
|
||||
invitation.accept!(
|
||||
params.require(:invitation).permit(:email, :password, :password_confirmation)
|
||||
)
|
||||
render json: invitation
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
module Api
|
||||
module V1
|
||||
class NotificationsController < ApplicationController
|
||||
def index
|
||||
notifications = Notification.where(encrypted_notify_user_id: current_user.encrypted_id)
|
||||
|
||||
authorize_collection :index, notifications
|
||||
|
||||
render json: {notifications: notifications.aggregated_by_kind_and_subject}
|
||||
end
|
||||
|
||||
def update
|
||||
notifications = Notification.where(notification_params)
|
||||
|
||||
authorize_collection :update, notifications
|
||||
|
||||
if notifications.update_all(unread: false)
|
||||
render json: {notifications: notifications.aggregated_by_kind_and_subject}
|
||||
else
|
||||
render json: {errors: notifications.map(&:errors).compact}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
notifications = Notification.where(notification_params)
|
||||
|
||||
authorize_collection :destroy, notifications
|
||||
|
||||
if notifications.destroy
|
||||
head :no_content
|
||||
else
|
||||
render json: {errors: notifications.map(&:errors).compact}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def notification_params
|
||||
parameters = params.permit(:notificateable_id, :notificateable_type)
|
||||
|
||||
parameters[:notificateable_type] = parameters[:notificateable_type].titleize
|
||||
parameters[:encrypted_notify_user_id] = current_user.encrypted_id
|
||||
|
||||
parameters
|
||||
end
|
||||
|
||||
def authorize_collection(name, collection)
|
||||
collection.each { |element| authorize! name, element }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
module Api
|
||||
module V1
|
||||
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
Devise.omniauth_providers.each do |provider|
|
||||
define_method provider do
|
||||
handle_omniauth
|
||||
end
|
||||
end
|
||||
|
||||
def failure
|
||||
Rails.logger.warn("Api::V1::OmniauthCallbacksController#failure: #{failure_message}".yellow)
|
||||
render json: {errors: failure_message}, status: 401
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def handle_omniauth
|
||||
user = User.find_for_database_authentication(email: email_param)
|
||||
if user && user.invitation_token.nil?
|
||||
render json: user, root: false, serializer: SessionSerializer
|
||||
else
|
||||
render json: {errors: "User not found"}, status: 401
|
||||
end
|
||||
end
|
||||
|
||||
def oauth_params
|
||||
@oauth_params ||= ActionController::Parameters.new(request.env["omniauth.auth"])
|
||||
end
|
||||
|
||||
def email_param
|
||||
oauth_params.fetch(:info).fetch(:email)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,60 @@
|
||||
module Api
|
||||
module V1
|
||||
class OracleRequestsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
serialization_scope :oracle_token
|
||||
|
||||
load_resource
|
||||
|
||||
def show
|
||||
render json: @oracle_request
|
||||
end
|
||||
|
||||
def create
|
||||
if oracle_token.present?
|
||||
@oracle_request.token = oracle_token
|
||||
else
|
||||
loop do
|
||||
@oracle_request.token = SecureRandom.uuid
|
||||
|
||||
break unless OracleRequest.where(token: @oracle_request.token).exists?
|
||||
end
|
||||
end
|
||||
|
||||
@oracle_request.save
|
||||
|
||||
render json: @oracle_request, serializer: OracleRequestWithTokenSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
if @oracle_request.can_edit?(oracle_token)
|
||||
@oracle_request.update!(create_params)
|
||||
|
||||
render json: @oracle_request
|
||||
else
|
||||
render json: {errors: "Unauthorized"}, status: :unauthorised
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:oracle_request).permit(
|
||||
:age,
|
||||
:sex_id,
|
||||
responce: [
|
||||
:name,
|
||||
:confidence,
|
||||
:correction
|
||||
],
|
||||
symptom_ids: []
|
||||
)
|
||||
end
|
||||
|
||||
def oracle_token
|
||||
request.headers["X-Oracle-Token"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,56 @@
|
||||
module Api
|
||||
module V1
|
||||
class PasswordsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def show
|
||||
user = user_signed_in? ? current_user : User.with_reset_password_token(params[:id])
|
||||
|
||||
if user.blank?
|
||||
raise ActiveRecord::RecordNotFound, "User not found"
|
||||
else
|
||||
render json: user, token: params[:id], serializer: PasswordSerializer
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
user = User.find_by!(email: email_param.downcase)
|
||||
|
||||
return unless user.send_reset_password_instructions
|
||||
|
||||
render json: user, serializer: PasswordSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
if user_signed_in?
|
||||
if current_user.update_with_password(update_password_params)
|
||||
render json: current_user, token: params[:id], serializer: PasswordSerializer
|
||||
else
|
||||
render json: {errors: current_user.errors}, status: :unprocessable_entity
|
||||
end
|
||||
else
|
||||
user = User.reset_password_by_token(update_password_by_token_params)
|
||||
if user.errors.empty?
|
||||
render json: user, token: params[:id], serializer: PasswordSerializer
|
||||
else
|
||||
render json: {errors: user.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def email_param
|
||||
params.require(:password).fetch(:email)
|
||||
end
|
||||
|
||||
def update_password_params
|
||||
params.require(:password).permit(:current_password, :password, :password_confirmation)
|
||||
end
|
||||
|
||||
def update_password_by_token_params
|
||||
params.require(:password).permit(:reset_password_token, :password, :password_confirmation)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,68 @@
|
||||
module Api
|
||||
module V1
|
||||
class PatternsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
|
||||
def index
|
||||
page = params[:page] || 1
|
||||
pattern_ids = params[:pattern_ids]
|
||||
|
||||
@patterns =
|
||||
if pattern_ids.present?
|
||||
Pattern.where(id: {"$in" => pattern_ids})
|
||||
else
|
||||
Pattern.accessible_by(current_ability).where(encrypted_user_id: encrypted_user_id)
|
||||
end
|
||||
|
||||
render json: @patterns.page(page).per(10)
|
||||
end
|
||||
|
||||
def show
|
||||
pattern = Pattern.find_by(id: pattern_params[:id])
|
||||
|
||||
render json: pattern
|
||||
end
|
||||
|
||||
def create
|
||||
@pattern = PatternCreator.new(pattern_params.to_h).create
|
||||
|
||||
render json: @pattern
|
||||
end
|
||||
|
||||
def update
|
||||
@pattern.update(pattern_params)
|
||||
|
||||
render json: @pattern
|
||||
end
|
||||
|
||||
def destroy
|
||||
pattern = Pattern.find_by(id: params[:id])
|
||||
|
||||
authorize! :destroy, pattern
|
||||
|
||||
if pattern.destroy
|
||||
head :no_content
|
||||
else
|
||||
render json: {errors: pattern.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pattern_params
|
||||
params.require(:pattern)
|
||||
.permit(:name, :start_at, :end_at, includes: [:id, :category, :label])
|
||||
.merge(user_id: current_user.id)
|
||||
end
|
||||
|
||||
def current_ability
|
||||
@current_ability ||= Ability.new(current_user)
|
||||
end
|
||||
|
||||
def encrypted_user_id
|
||||
@encrypted_user_id ||= SymmetricEncryption.encrypt(current_user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
module Api
|
||||
module V1
|
||||
class PostablesController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def index
|
||||
render json: PostableSerializer.new(
|
||||
@postables
|
||||
.where(encrypted_user_id: current_user.encrypted_id)
|
||||
.order_by(created_at: :desc)
|
||||
.page(params[:page])
|
||||
.per(20),
|
||||
current_user
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
module Api
|
||||
module V1
|
||||
class PostsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:index, :show]
|
||||
|
||||
def index
|
||||
if params[:summary]
|
||||
render json: SummaryPosts.new(current_user).show_list
|
||||
else
|
||||
@posts = DiscussionPosts.new(params, current_user).show_list
|
||||
|
||||
results = @posts
|
||||
.includes([:comments, :notifications, :reactions])
|
||||
.order(last_commented: :desc, created_at: :desc)
|
||||
.page(params[:page])
|
||||
.per(10)
|
||||
render json: results
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @post
|
||||
end
|
||||
|
||||
def create
|
||||
@post.encrypted_user_id = current_user.encrypted_id
|
||||
|
||||
if @post.save
|
||||
render json: @post, status: :created
|
||||
else
|
||||
render json: {errors: @post.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:post).permit(
|
||||
:title, :body,
|
||||
tag_ids: [], symptom_ids: [], condition_ids: [], treatment_ids: []
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
module Api
|
||||
module V1
|
||||
class ProfilesController < ApplicationController
|
||||
require "sidekiq/api"
|
||||
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
|
||||
def index
|
||||
post = Post.find(params[:post_id])
|
||||
|
||||
encrypted_user_ids =
|
||||
(post.comments.distinct(:encrypted_user_id) << post.encrypted_user_id).uniq.map do |encrypted_id|
|
||||
SymmetricEncryption.decrypt(encrypted_id)
|
||||
end
|
||||
|
||||
@profiles = Profile.where(user_id: encrypted_user_ids).where.not(slug_name: nil)
|
||||
render json: @profiles.map { |profile| profile.attributes.slice("screen_name", "slug_name") }
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @profile
|
||||
end
|
||||
|
||||
def update
|
||||
initial_onboarding_reminder = params.dig(:profile, :onboarding_reminder)
|
||||
|
||||
@profile.assign_attributes(update_params.merge(transform_hash_time))
|
||||
time_changed = @profile.checkin_reminder_at_changed? || @profile.time_zone_name_changed?
|
||||
@profile.save!
|
||||
|
||||
if time_changed || initial_onboarding_reminder
|
||||
delete_old_job(@profile.reminder_job_id)
|
||||
|
||||
job_id = CheckinReminderJob.perform_in(get_reminder_time.minutes, @profile.id, @profile.checkin_reminder_at)
|
||||
@profile.update_column(:reminder_job_id, job_id)
|
||||
end
|
||||
|
||||
current_user.profile.reload
|
||||
set_locale
|
||||
render json: @profile
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_params
|
||||
params.require(:profile).permit(
|
||||
:country_id, :sex_id, :onboarding_step_id, :birth_date,
|
||||
:day_habit_id, :education_level_id, :day_walking_hours,
|
||||
:pressure_units, :temperature_units, :screen_name, :notify,
|
||||
:checkin_reminder, :time_zone_name, :notify_top_posts, ethnicity_ids: []
|
||||
)
|
||||
end
|
||||
|
||||
def transform_hash_time
|
||||
checkin_reminder_at = params.require(:profile)[:checkin_reminder_at]
|
||||
user_time = checkin_reminder_at && checkin_reminder_at.values.join(":")
|
||||
|
||||
{checkin_reminder_at: user_time.try(:to_time, :utc)}
|
||||
end
|
||||
|
||||
def get_reminder_time
|
||||
time_zone_name = @profile.time_zone_name
|
||||
checkin_at_timezone = @profile.checkin_reminder_at.strftime("%H:%M").in_time_zone(time_zone_name)
|
||||
|
||||
# Select minutes
|
||||
(checkin_at_timezone - Time.current.in_time_zone(time_zone_name)).divmod(1.day)[1].divmod(1.minute)[0]
|
||||
end
|
||||
|
||||
def delete_old_job(enqueued_job_id)
|
||||
Sidekiq::ScheduledSet.new.find_job(enqueued_job_id)&.delete
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
module Api
|
||||
module V1
|
||||
class PromotionRatesController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def show
|
||||
render json: @promotion_rate
|
||||
end
|
||||
|
||||
def create
|
||||
@promotion_rate.save
|
||||
|
||||
render json: @promotion_rate
|
||||
end
|
||||
|
||||
def update
|
||||
@promotion_rate.update(resource_params.merge(additional_params))
|
||||
|
||||
render json: @promotion_rate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:promotion_rate).permit(:checkin_id, :score, :feedback)
|
||||
end
|
||||
|
||||
def additional_params
|
||||
user = @promotion_rate.checkin.user
|
||||
|
||||
{user_created_at: user.created_at}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
module Api
|
||||
module V1
|
||||
class PushersController < ApplicationController
|
||||
def create
|
||||
render json: Flaredown.pusher.authenticate!(current_user, socket_id)
|
||||
rescue
|
||||
render json: {errors: "Bad authentication"}, status: "403"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def socket_id
|
||||
params.require(:socket_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,77 @@
|
||||
module Api
|
||||
module V1
|
||||
class ReactionsController < ApplicationController
|
||||
def create
|
||||
react(__method__)
|
||||
end
|
||||
|
||||
def update
|
||||
react(__method__)
|
||||
end
|
||||
|
||||
def destroy
|
||||
reaction = Reaction.where(reaction_params).first
|
||||
|
||||
authorize! :destroy, reaction
|
||||
|
||||
if reaction.destroy
|
||||
UpdatePostCountersJob.perform_async(parent_id: reaction_params[:reactable_id],
|
||||
parent_type: reaction_params[:reactable_type])
|
||||
|
||||
head :no_content
|
||||
else
|
||||
render json: {errors: reaction.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def react(method_name)
|
||||
reaction = Reaction.find_or_initialize_by(reaction_params)
|
||||
|
||||
authorize! method_name, reaction
|
||||
|
||||
if reaction.save
|
||||
UpdatePostCountersJob.perform_async(parent_id: reaction_params[:reactable_id],
|
||||
parent_type: reaction_params[:reactable_type])
|
||||
|
||||
unless reaction.encrypted_user_id == reaction.reactable.encrypted_user_id
|
||||
Notification.create(
|
||||
kind: :reaction,
|
||||
notificateable: reaction.reactable,
|
||||
encrypted_user_id: reaction.encrypted_user_id,
|
||||
encrypted_notify_user_id: reaction.reactable.encrypted_user_id
|
||||
)
|
||||
end
|
||||
|
||||
reaction.id = params[:id] if params[:id].present?
|
||||
|
||||
render json: serialized_reaction(reaction), status: :created
|
||||
else
|
||||
render json: {errors: reaction.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def reaction_params
|
||||
reaction = params.require(:reaction)
|
||||
|
||||
{
|
||||
value: reaction[:value],
|
||||
reactable_id: reaction[:reactable_id],
|
||||
reactable_type: reaction[:reactable_type].titleize,
|
||||
encrypted_user_id: current_user.encrypted_id
|
||||
}
|
||||
end
|
||||
|
||||
def serialized_reaction(reaction)
|
||||
ReactionSerializer
|
||||
.new(
|
||||
Reaction.similar_to(reaction).values_count_with_participated(current_user.encrypted_id),
|
||||
reaction.reactable_id.to_s,
|
||||
reaction.reactable_type
|
||||
)
|
||||
.serialize_one
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
module Api
|
||||
module V1
|
||||
class RegistrationsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
render json: Registration.create!(params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
render json: Registration.delete!(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
module Api
|
||||
module V1
|
||||
class SearchesController < ApplicationController
|
||||
SEARCH_MAPPER = {
|
||||
"dose" => Search::ForDose,
|
||||
"food" => Search::ForFood,
|
||||
"topic" => Search::ForTopic
|
||||
}.freeze
|
||||
|
||||
skip_before_action :authenticate_user!, only: :show
|
||||
|
||||
def show
|
||||
search = (SEARCH_MAPPER[resource_param] || Search).new(search_params)
|
||||
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActiveRecord::RecordInvalid, search) if search.invalid?
|
||||
# rubocop:enable Style/SignalException
|
||||
|
||||
render json: search, serializer: SearchSerializer
|
||||
end
|
||||
|
||||
def search_params
|
||||
params.permit(:resource, :scope, query: [:name, :treatment_id]).tap do |params|
|
||||
params[:user] = current_user
|
||||
end
|
||||
end
|
||||
|
||||
def resource_param
|
||||
params[:resource]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
module Api
|
||||
module V1
|
||||
class SessionsController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail "missing information" if params[:user].nil?
|
||||
fail "invalid email or password" if user.nil?
|
||||
# rubocop:enable Style/SignalException
|
||||
|
||||
render json: user, root: false, serializer: SessionSerializer
|
||||
rescue => e
|
||||
render json: {errors: Array(e.message)}, status: 401
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user
|
||||
@user ||=
|
||||
begin
|
||||
user = User.find_for_database_authentication(email: params[:user][:email])
|
||||
user if user && user.valid_password?(params[:user][:password])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
module Api
|
||||
module V1
|
||||
class SexesController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def index
|
||||
render json: Sex.all
|
||||
end
|
||||
|
||||
def show
|
||||
sex = Sex.find(sex_id)
|
||||
render json: sex
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sex_id
|
||||
id = params.require(:id)
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail(ActionController::BadRequest, "id param is not a valid sex id") unless Sex.all_ids.include?(id)
|
||||
# rubocop:enable Style/SignalException
|
||||
id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
module Api
|
||||
module V1
|
||||
class SymptomsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:show]
|
||||
|
||||
def index
|
||||
@symptoms = @symptoms.includes(:translations)
|
||||
@symptoms = ids.present? ? @symptoms.where(id: ids) : @symptoms.order(:name).limit(50)
|
||||
|
||||
render json: @symptoms
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @symptom
|
||||
end
|
||||
|
||||
def create
|
||||
render json: TrackableCreator.new(@symptom, current_user).create!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:symptom).permit(:name)
|
||||
end
|
||||
|
||||
def ids
|
||||
@ids ||= params[:ids] if params[:ids].is_a?(Array)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
module Api
|
||||
module V1
|
||||
class TagsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:show]
|
||||
|
||||
def index
|
||||
@tags = @tags.includes(:translations)
|
||||
if ids.present?
|
||||
@tags = @tags.where(id: ids)
|
||||
elsif scope.present?
|
||||
@tags = CollectionRetriever.new(Tag, scope, current_user).retrieve
|
||||
end
|
||||
render json: @tags
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @tag
|
||||
end
|
||||
|
||||
def create
|
||||
render json: TrackableCreator.new(@tag, current_user).create!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:tag).permit(:name)
|
||||
end
|
||||
|
||||
def ids
|
||||
@ids ||= params[:ids]
|
||||
end
|
||||
|
||||
def scope
|
||||
@scope ||= params[:scope].try(:to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
module Api
|
||||
module V1
|
||||
class TopicFollowingsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def show
|
||||
render json: @topic_following
|
||||
end
|
||||
|
||||
def update
|
||||
if @topic_following.update(update_params)
|
||||
render json: @topic_following, status: :ok
|
||||
else
|
||||
render json: {errors: @topic_following.errors}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_params
|
||||
empty_params = {
|
||||
"tag_ids" => [],
|
||||
"symptom_ids" => [],
|
||||
"condition_ids" => [],
|
||||
"treatment_ids" => []
|
||||
}
|
||||
|
||||
empty_params.merge(params.require(:topic_following).permit(empty_params))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
module Api
|
||||
module V1
|
||||
class TrackingsController < ApplicationController
|
||||
load_and_authorize_resource except: :create
|
||||
|
||||
def index
|
||||
render json: @trackings.by_trackable_type(trackable_type).active_at(at)
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @tracking
|
||||
end
|
||||
|
||||
def create
|
||||
tracking = Tracking.new(create_params.merge(start_at: Time.zone.today, user: current_user))
|
||||
|
||||
authorize! :create, tracking
|
||||
|
||||
tracking.save!
|
||||
|
||||
current_user.topic_following.add_topic("#{tracking.trackable_type.downcase}_ids", tracking.trackable_id)
|
||||
|
||||
render json: tracking
|
||||
end
|
||||
|
||||
def destroy
|
||||
TrackingDestroyer.new(current_user, @tracking, Time.zone.today).destroy
|
||||
head :no_content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def at
|
||||
Time.zone.parse(params.require(:at))
|
||||
end
|
||||
|
||||
def trackable_type
|
||||
params.require(:trackable_type)
|
||||
end
|
||||
|
||||
def create_params
|
||||
params.require(:tracking).permit(:trackable_id, :trackable_type, :color_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
module Api
|
||||
module V1
|
||||
class TreatmentsController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
skip_before_action :authenticate_user!, only: [:show]
|
||||
|
||||
def index
|
||||
@treatments = @treatments.includes(:translations)
|
||||
@treatments = ids.present? ? @treatments.where(id: ids) : @treatments.order(:name).limit(50)
|
||||
|
||||
render json: @treatments
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @treatment
|
||||
end
|
||||
|
||||
def create
|
||||
render json: TrackableCreator.new(@treatment, current_user).create!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
params.require(:treatment).permit(:name)
|
||||
end
|
||||
|
||||
def ids
|
||||
@ids ||= params[:ids] if params[:ids].is_a?(Array)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
module Api
|
||||
module V1
|
||||
class UnsubscribesController < ApplicationController
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def update
|
||||
@profile = Profile.find_by(notify_token: activation_params[:notify_token])
|
||||
@profile&.update_column(attribute_dispatcher_key, false)
|
||||
|
||||
render json: @profile
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def activation_params
|
||||
params.permit(:notify_token, :notify_top_posts, :stop_remind)
|
||||
end
|
||||
|
||||
def attribute_dispatcher_key
|
||||
if activation_params[:stop_remind]
|
||||
:checkin_reminder
|
||||
elsif activation_params[:notify_top_posts]
|
||||
:notify_top_posts
|
||||
else
|
||||
:notify
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
module Api
|
||||
module V1
|
||||
class UsersController < ApplicationController
|
||||
load_and_authorize_resource
|
||||
|
||||
def show
|
||||
render json: @user
|
||||
end
|
||||
|
||||
def update
|
||||
@user.update!(user_params)
|
||||
|
||||
render json: @user
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:email)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
module Api
|
||||
module V1
|
||||
class WeathersController < ApplicationController
|
||||
def index
|
||||
unless params[:postal_code].blank?
|
||||
weather = WeatherRetriever.get(Date.parse(params.require(:date)), params.require(:postal_code))
|
||||
end
|
||||
|
||||
authorize! :read, weather
|
||||
|
||||
if weather.present?
|
||||
render json: weather
|
||||
else
|
||||
render json: {weathers: []}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
class ApplicationController < ActionController::API
|
||||
include CanCan::ControllerAdditions
|
||||
include ActionController::Serialization
|
||||
include ExceptionLogger
|
||||
|
||||
before_action :authenticate_user_from_token!, if: :presence_of_authentication_token?
|
||||
before_action :authenticate_user!, except: [:root]
|
||||
before_action :set_locale
|
||||
|
||||
def root
|
||||
render json: {
|
||||
ios: {
|
||||
major: 0,
|
||||
minor: 0
|
||||
},
|
||||
android: {
|
||||
major: 0,
|
||||
minor: 0
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_locale
|
||||
I18n.locale = user_signed_in? ? current_user.locale : I18n.default_locale
|
||||
rescue I18n::InvalidLocale
|
||||
# FIXME
|
||||
Rails.logger.warn("'#{current_user.profile.locale}' locale for user '#{current_user.email}' not available or invalid, using default")
|
||||
I18n.locale = I18n.default_locale
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate_user_from_token!
|
||||
user = User.find_by(authorization)
|
||||
sign_in user, store: false if user
|
||||
end
|
||||
|
||||
def authorization
|
||||
@authorization ||=
|
||||
/^Token token="(?<token>.*)", email="(?<email>.*)"$/.match(request.headers["Authorization"]) || {}
|
||||
|
||||
{authentication_token: @authorization[:token], email: @authorization[:email]}
|
||||
end
|
||||
|
||||
def presence_of_authentication_token?
|
||||
authorization[:authentication_token].present?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,56 @@
|
||||
module ExceptionLogger
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
# keep this on top
|
||||
if Rails.env.production?
|
||||
rescue_from "Exception" do |exception|
|
||||
render json: {errors: exception.message}, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
rescue_from "ActiveRecord::RecordNotFound", "Mongoid::Errors::DocumentNotFound" do
|
||||
render json: {errors: ["Resource Not Found"]}, status: 404
|
||||
end
|
||||
|
||||
rescue_from "ActionController::UnknownFormat" do
|
||||
render json: {errors: ["Format not supported"]}, status: 406
|
||||
end
|
||||
|
||||
rescue_from "ActiveRecord::RecordInvalid", "Mongoid::Errors::Validations" do |exception|
|
||||
log_exception(exception)
|
||||
render json: {errors: exception.record.errors}, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
rescue_from "ActionController::ParameterMissing" do |exception|
|
||||
render json: {errors: ["Required parameter missing: #{exception.param}"]}, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
rescue_from "ActionController::BadRequest" do |exception|
|
||||
render json: {errors: ["Bad request: #{exception.message}"]}, status: :bad_request
|
||||
end
|
||||
|
||||
rescue_from "CanCan::AccessDenied" do |exception|
|
||||
log_exception(exception)
|
||||
render json: {errors: ["Unauthorized"]}, status: :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# --- FOR RAILS 4: ---
|
||||
## @exception = env["action_dispatch.exception"]
|
||||
## exception_wrapper = ActionDispatch::ExceptionWrapper.new(env, @exception)
|
||||
|
||||
# --- FOR RAILS 5: ---
|
||||
|
||||
def log_exception(exception)
|
||||
application_trace = ActionDispatch::ExceptionWrapper.new(request.env["action_dispatch.backtrace_cleaner"], exception)
|
||||
|
||||
trace = application_trace.application_trace
|
||||
|
||||
trace = trace.map! { |t| " #{t}\n" }
|
||||
|
||||
Rails.logger.error "\n#{exception.class.name} (#{exception.message}):\n#{trace.join}"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
module PatternExtender
|
||||
attr_accessor :start_at, :end_at, :pattern, :user, :chart_data
|
||||
|
||||
def form_chart_data(options)
|
||||
@start_at = options[:start_at]
|
||||
@end_at = options[:end_at]
|
||||
@pattern = options[:pattern]
|
||||
@user = options[:user]
|
||||
|
||||
@chart_data = ChartsPattern.new(start_at: start_at, end_at: end_at, pattern: pattern, user: user).chart_data
|
||||
|
||||
chart_data.merge(used_color_ids: used_color_ids)
|
||||
end
|
||||
|
||||
def used_color_ids
|
||||
chart_data[:series].map { |item| item[:color_id] }.compact
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
module PostExtender
|
||||
attr_reader :priority
|
||||
|
||||
def frequency_topic_priority(priority)
|
||||
@priority = priority
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
class ApplicationJob < ActiveJob::Base
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
class CheckinReminderJob
|
||||
require "sidekiq/api"
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(profile_id, checkin_reminder_at)
|
||||
profile = Profile.find_by(id: profile_id)
|
||||
|
||||
return unless profile
|
||||
return unless profile.checkin_reminder
|
||||
return if profile.rejected_type.present?
|
||||
|
||||
return unless jid == profile.reminder_job_id
|
||||
|
||||
CheckinReminderMailer.remind(email: profile.email).deliver_later
|
||||
|
||||
profile.update_column(:reminder_job_id, self.class.perform_in(24.hours, profile_id, checkin_reminder_at))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,138 @@
|
||||
class DataExportJob < ActiveJob::Base
|
||||
SUBFIELD_SEPARATOR = "; ".freeze
|
||||
|
||||
queue_as :default
|
||||
|
||||
attr_accessor :user_locale, :symptom_ids, :condition_ids, :treatment_ids, :tag_ids, :food_ids
|
||||
|
||||
def perform(user_id)
|
||||
user = User.find(user_id)
|
||||
|
||||
data = csv_data(user)
|
||||
|
||||
UserDataMailer.trackings_csv(user.email, data).deliver_later
|
||||
end
|
||||
|
||||
def csv_data(user)
|
||||
checkins = user.checkins.includes(:conditions, :symptoms, :treatments).order_by(date: :asc)
|
||||
|
||||
set_attributes(user.locale, checkins)
|
||||
|
||||
CSV.generate do |csv|
|
||||
csv << headers
|
||||
|
||||
checkins.each { |checkin| csv << checkin_row(checkin) }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def checkin_row(checkin)
|
||||
row = [checkin.date]
|
||||
|
||||
symptoms_map = trackables_map(checkin.symptoms, "symptom")
|
||||
conditions_map = trackables_map(checkin.conditions, "condition")
|
||||
treatments_map = treatments_map(checkin.treatments)
|
||||
|
||||
condition_names.keys.each { |id| row << conditions_map[id] }
|
||||
symptom_names.keys.each { |id| row << symptoms_map[id] }
|
||||
treatment_names.keys.each { |id| row << treatments_map[id] }
|
||||
|
||||
row << tag_names.slice(*checkin.tag_ids).values.join(SUBFIELD_SEPARATOR)
|
||||
row << food_names.slice(*checkin.food_ids).values.join(SUBFIELD_SEPARATOR)
|
||||
|
||||
row << checkin.weather&.summary
|
||||
row << checkin.weather&.temperature_max
|
||||
row << checkin.weather&.temperature_min
|
||||
row << checkin.weather&.pressure
|
||||
row << checkin.weather&.precip_intensity
|
||||
row << checkin.weather&.humidity
|
||||
end
|
||||
|
||||
def set_attributes(locale, checkins)
|
||||
checkin_ids = checkins.pluck(:id)
|
||||
|
||||
self.user_locale = I18n.locale_available?(locale) ? locale : I18n.default_locale
|
||||
|
||||
self.symptom_ids = Checkin::Symptom.in(checkin_id: checkin_ids).distinct(:symptom_id)
|
||||
self.condition_ids = Checkin::Condition.in(checkin_id: checkin_ids).distinct(:condition_id)
|
||||
self.treatment_ids = Checkin::Treatment.in(checkin_id: checkin_ids).distinct(:treatment_id)
|
||||
|
||||
self.tag_ids = checkins.distinct(:tag_ids)
|
||||
self.food_ids = checkins.distinct(:food_ids)
|
||||
end
|
||||
|
||||
def symptom_names
|
||||
@symptom_names ||= object_names(Symptom, :name, symptom_ids)
|
||||
end
|
||||
|
||||
def condition_names
|
||||
@condition_names ||= object_names(Condition, :name, condition_ids)
|
||||
end
|
||||
|
||||
def treatment_names
|
||||
@treatment_names ||= object_names(Treatment, :name, treatment_ids)
|
||||
end
|
||||
|
||||
def tag_names
|
||||
@tag_names ||= object_names(Tag, :name, tag_ids)
|
||||
end
|
||||
|
||||
def food_names
|
||||
@food_names ||= object_names(Food, :long_desc, food_ids)
|
||||
end
|
||||
|
||||
def object_names(klass, field, ids)
|
||||
name_slug = klass.name.underscore
|
||||
relation = :"#{name_slug}_translations"
|
||||
|
||||
# .pluck(:"#{name_slug}_id", field)
|
||||
klass
|
||||
.joins(relation)
|
||||
.where(:id => ids, relation => {locale: user_locale})
|
||||
.pluck(:id, field)
|
||||
.to_h
|
||||
end
|
||||
|
||||
def headers
|
||||
weather_headers = [
|
||||
"Weather summary",
|
||||
"Weather max temperature",
|
||||
"Weather min temperature",
|
||||
"Weather pressure",
|
||||
"Weather precipitation intensity",
|
||||
"Weather humidity"
|
||||
]
|
||||
|
||||
["Date"]
|
||||
.concat(condition_names.values)
|
||||
.concat(symptom_names.values)
|
||||
.concat(treatment_names.values)
|
||||
.concat(%w[Tags Foods])
|
||||
.concat(weather_headers)
|
||||
end
|
||||
|
||||
def trackables_map(trackables, type)
|
||||
id_field = "#{type}_id"
|
||||
|
||||
trackables.map { |c| [c[id_field], c.value] }.to_h
|
||||
end
|
||||
|
||||
# special handling to allow distinguishing between treatments with no dosage, and treatments not taken
|
||||
def treatments_map(treatments)
|
||||
treatments.map do |treatment|
|
||||
[
|
||||
treatment["treatment_id"],
|
||||
treatment_value(treatment.is_taken, treatment.value)
|
||||
]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
def treatment_value(is_taken, value)
|
||||
if is_taken
|
||||
value || "Taken"
|
||||
else
|
||||
"Not Taken"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
class DiscussionMention
|
||||
include Sidekiq::Worker
|
||||
|
||||
NAME_REGEXP = /@\w+/
|
||||
|
||||
def perform(encrypted_user_id, comment_id)
|
||||
comment = Comment.find(comment_id)
|
||||
profiles = found_profiles(comment.body)
|
||||
return if profiles.empty?
|
||||
|
||||
create_notifications(encrypted_user_id, profiles.map(&:user_id), comment)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def found_profiles(mentioned_string)
|
||||
selected_names = mentioned_string.scan(NAME_REGEXP).uniq
|
||||
|
||||
selected_names.each_with_object([]) do |matched_string, result_array|
|
||||
matched_string.slice!(0) # remove '@ tag'
|
||||
|
||||
profile = Profile.find_by(slug_name: matched_string)
|
||||
result_array << profile if profile
|
||||
end
|
||||
end
|
||||
|
||||
def create_notifications(encrypted_user_id, user_ids, comment_id)
|
||||
encrypted_notify_user_ids = user_ids.map { |user_id| SymmetricEncryption.encrypt(user_id) }
|
||||
comment = Comment.find(comment_id)
|
||||
|
||||
encrypted_notify_user_ids.map do |encrypted_notify_user_id|
|
||||
Notification.create(
|
||||
kind: :mention,
|
||||
notificateable: comment,
|
||||
encrypted_user_id: encrypted_user_id,
|
||||
encrypted_notify_user_id: encrypted_notify_user_id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
class EmailRejectDispatcher
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(raw_post)
|
||||
body = JSON.parse(raw_post)
|
||||
test_case_type = body["notificationType"]
|
||||
message_raw = body["Message"]
|
||||
|
||||
recipients =
|
||||
if test_case_type == "Bounce"
|
||||
emails = body.dig("mail", "destination") || []
|
||||
|
||||
{bounce: emails}
|
||||
elsif message_raw
|
||||
generate_recipients(message_raw)
|
||||
end
|
||||
|
||||
return unless recipients.present?
|
||||
|
||||
EmailRejectJob.perform_async(recipients)
|
||||
end
|
||||
|
||||
def generate_recipients(message_raw)
|
||||
message = JSON.parse message_raw
|
||||
|
||||
emails = message.dig("mail", "destination") || []
|
||||
rejected_type = message["notificationType"].downcase
|
||||
|
||||
{rejected_type.to_sym => emails}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class EmailRejectJob
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(recipients_hash)
|
||||
User.where(email: recipients_hash.values.first).includes(:profile).map do |user|
|
||||
user.profile.update_column(:rejected_type, recipients_hash.keys.first.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
class GroupNotifiersPerUser
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(encrypted_user_id)
|
||||
begin
|
||||
user_id = SymmetricEncryption.decrypt(encrypted_user_id)
|
||||
rescue OpenSSL::Cipher::CipherError
|
||||
return
|
||||
end
|
||||
|
||||
user = User.find_by(id: user_id)
|
||||
return unless user&.notify
|
||||
return if user&.rejected_type.present? # Rejected via AWS SES
|
||||
|
||||
active_notifications = Notification.where(encrypted_notify_user_id: encrypted_user_id, delivered: false)
|
||||
return if active_notifications.none?
|
||||
|
||||
aggregate_data = active_notifications.groupped_by_post_and_kind(encrypted_user_id)
|
||||
|
||||
NotificationsMailer.notify(email: user.email, data: aggregate_data).deliver_later
|
||||
|
||||
clean_notifications(active_notifications)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clean_notifications(active_notifications)
|
||||
active_notifications.batch_size(500).each { |notification| notification.update(delivered: true) }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
class GroupTopPostsJob
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(notify_token)
|
||||
profile = Profile.find_by(notify_token: notify_token)
|
||||
user = profile&.user
|
||||
|
||||
return unless profile && profile.notify_top_posts && user
|
||||
return if user&.rejected_type.present? # Rejected via AWS SES
|
||||
|
||||
top_posts_ids = DiscussionPosts.new(nil, user).refined_top_list.map(&:id).map(&:to_s)
|
||||
TopPostsMailer.notify(email: user.email, notify_token: notify_token, top_posts_ids: top_posts_ids).deliver_later
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
class HelloWorldJob < ActiveJob::Base
|
||||
queue_as :default
|
||||
|
||||
def perform(*_args)
|
||||
Rails.logger.debug("Hello World".green)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
class MergeTrackables::CheckinTrackables
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
if Checkin::FIELD_TYPE.include? trackable_type
|
||||
update_health_factors(trackable_type, parent_id, rest_ids)
|
||||
else
|
||||
update_regular_trackables(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::PostTrackables"
|
||||
|
||||
MergeTrackables::PatternIncludes.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
|
||||
def update_health_factors(trackable_type, parent_id, rest_ids)
|
||||
Checkin.where("#{trackable_type}_ids" => {"$in" => rest_ids}).map do |checkin|
|
||||
updated_ids = checkin.send("#{trackable_type}_ids") - rest_ids + [parent_id]
|
||||
|
||||
checkin.update("#{trackable_type}_ids".to_sym => updated_ids.uniq)
|
||||
end
|
||||
end
|
||||
|
||||
def update_regular_trackables(trackable_type, parent_id, rest_ids)
|
||||
klass = "Checkin::#{trackable_type.capitalize}".constantize
|
||||
return unless klass
|
||||
|
||||
klass.where("#{trackable_type}_id".to_sym.in => rest_ids).map do |checkin|
|
||||
checkin.update("#{trackable_type}_id".to_sym => parent_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
class MergeTrackables::Dispatcher
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, translation = nil)
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
searchable_attr = (trackable_class.name == "Food") ? "long_desc" : "name"
|
||||
|
||||
return find_duplicates(trackable_type, trackable_class, translation, searchable_attr) if translation.present?
|
||||
|
||||
trackable_class::Translation.find_each do |trackable_translation|
|
||||
translation = trackable_translation.send(searchable_attr.to_s)
|
||||
next unless translation
|
||||
|
||||
find_duplicates(trackable_type, trackable_class, translation, searchable_attr)
|
||||
end
|
||||
end
|
||||
|
||||
def find_duplicates(trackable_type, trackable_class, translation, searchable_attr = "name")
|
||||
begin
|
||||
escaped_translation = Regexp.escape(translation.squish).split(" ").join("s+")
|
||||
regex = "^\\s*#{escaped_translation}\\s*$"
|
||||
|
||||
same_translations = trackable_class::Translation.where("#{searchable_attr} ~* ?", regex)
|
||||
|
||||
return if same_translations.length <= 1 # Next step if origin found only
|
||||
|
||||
same_trackables = trackable_class
|
||||
.where(id: same_translations.where(locale: "en").select("#{trackable_type}_id".to_sym))
|
||||
.order(trackable_usages_count: :desc, id: :asc)
|
||||
|
||||
parent, *rest = same_trackables
|
||||
return if parent.nil? || rest.length.zero?
|
||||
|
||||
p "DUPLICATES FOUND..."
|
||||
p "#{trackable_type.capitalize} ids: #{same_trackables.pluck(:id)}"
|
||||
p "#{trackable_type.capitalize} translations: }"
|
||||
p same_trackables.map(&:"#{searchable_attr}")
|
||||
|
||||
rest_ids = rest.map(&:id)
|
||||
|
||||
# Remove duplicates from translations
|
||||
same_translations.where("#{trackable_type}_id": rest_ids).map(&:destroy)
|
||||
rescue ActiveRecord::StatementInvalid
|
||||
return
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::UserTrackableAssociation"
|
||||
MergeTrackables::UserTrackableAssociation.perform_async(trackable_type, parent.id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
class MergeTrackables::PatternIncludes
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
parent = trackable_type.capitalize.constantize.find_by(id: parent_id)
|
||||
return unless parent
|
||||
|
||||
Pattern.where("includes.id" => {"$in" => rest_ids}).map do |item|
|
||||
item.includes
|
||||
.select { |hash| hash[:category] == trackable_type.tableize && rest_ids.include?(hash[:id]) }
|
||||
.map do |selected_hash|
|
||||
selected_hash[:id] = parent_id
|
||||
selected_hash[:label] = parent.name
|
||||
end
|
||||
|
||||
item.includes = item.includes.uniq
|
||||
item.save
|
||||
end
|
||||
|
||||
MergeTrackables::PostTrackables.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
class MergeTrackables::PostTrackables
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
trackable_key = "#{trackable_type}_ids"
|
||||
|
||||
Post.where(trackable_key.to_sym.in => rest_ids).map do |post|
|
||||
updated_ids = post.send(trackable_key) - rest_ids + [parent_id]
|
||||
|
||||
post.update(trackable_key.to_sym => updated_ids.uniq)
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::RemoveDuplicates"
|
||||
|
||||
MergeTrackables::RemoveDuplicates.perform_async(trackable_type, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
class MergeTrackables::RemoveDuplicates
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, rest_ids)
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
return unless trackable_class
|
||||
|
||||
trackable_class.where(id: rest_ids).map(&:destroy)
|
||||
p "FINISH!!!!"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
class MergeTrackables::TopicFollowing
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
trackable_key = "#{trackable_type}_ids"
|
||||
|
||||
TopicFollowing.all(trackable_key.to_sym.in => rest_ids).map do |tf|
|
||||
updated_ids = tf.send(trackable_key) - rest_ids + [parent_id]
|
||||
|
||||
tf.update(trackable_key.to_sym => updated_ids.uniq)
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::CheckinTrackables"
|
||||
|
||||
MergeTrackables::CheckinTrackables.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
class MergeTrackables::TrackableUsages
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
return unless trackable_class
|
||||
|
||||
parent = trackable_class.find_by(id: parent_id)
|
||||
rest = trackable_class.where(id: rest_ids)
|
||||
return if parent.nil? && rest.length.zero?
|
||||
|
||||
TrackableUsage.where(trackable: rest).map do |tr_usage|
|
||||
parent_usage = TrackableUsage.find_by(trackable: parent, user_id: tr_usage.user_id)
|
||||
|
||||
if parent_usage
|
||||
parent_usage.count += tr_usage.count
|
||||
parent_usage.save
|
||||
|
||||
tr_usage.destroy
|
||||
else
|
||||
tr_usage.update(trackable_id: parent.id)
|
||||
|
||||
parent.increment!(:trackable_usages_count)
|
||||
end
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::Trackings"
|
||||
|
||||
MergeTrackables::Trackings.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
class MergeTrackables::Trackings
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
Tracking.where(trackable_id: rest_ids, trackable_type: trackable_type.camelcase).map do |tracking|
|
||||
tracking.update(trackable_id: parent_id)
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::TopicFollowing"
|
||||
|
||||
MergeTrackables::TopicFollowing.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
class MergeTrackables::UserTrackableAssociation
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(trackable_type, parent_id, rest_ids)
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
klass = "User#{trackable_type.capitalize}".constantize
|
||||
return unless trackable_class && klass
|
||||
|
||||
parent = trackable_class.find_by(id: parent_id)
|
||||
rest = trackable_class.where(id: rest_ids)
|
||||
return if parent.nil? && rest.length.zero?
|
||||
|
||||
klass.where(trackable_type.to_sym => rest).map do |user_trackable_type|
|
||||
parent_user_trackable = klass.find_by(trackable_type.to_sym => parent,
|
||||
:user_id => user_trackable_type.user_id)
|
||||
|
||||
if parent_user_trackable.present?
|
||||
user_trackable_type.destroy
|
||||
else
|
||||
user_trackable_type.update_columns("#{trackable_type}_id".to_sym => parent.id)
|
||||
end
|
||||
end
|
||||
|
||||
p "PERFORM MergeTrackables::TrackableUsages"
|
||||
MergeTrackables::TrackableUsages.perform_async(trackable_type, parent_id, rest_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class NotesExportJob < ActiveJob::Base
|
||||
SUBFIELD_SEPARATOR = "; ".freeze
|
||||
|
||||
queue_as :default
|
||||
|
||||
attr_accessor :user_locale, :symptom_ids, :condition_ids, :treatment_ids, :tag_ids, :food_ids
|
||||
|
||||
def perform(user_id)
|
||||
user = User.find(user_id)
|
||||
note_details = user.checkins.where.not(note: ["", nil]).order(date: :desc).pluck(:date, :note)
|
||||
body = note_details.map { |detail| detail.join(",") }
|
||||
|
||||
ActionMailer::Base.mail(
|
||||
from: Rails.application.secrets.smtp_email_from,
|
||||
to: user.email,
|
||||
subject: "Flaredown data export",
|
||||
body: body
|
||||
).deliver
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class NotificationDispatcher
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform
|
||||
Notification.distinct(:encrypted_notify_user_id).each do |encrypted_id|
|
||||
GroupNotifiersPerUser.perform_async(encrypted_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
class PositionReferenceJob
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(object_class, objec_id, postal_code)
|
||||
object = object_class.constantize.find_by(id: objec_id)
|
||||
position = Position.find_or_create_by(postal_code: postal_code)
|
||||
|
||||
object.update(position_id: position.id) if position.persisted?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
class SameTrackablesJob
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(options)
|
||||
trackable_type = options["trackable_type"]
|
||||
translation = options["translation"]
|
||||
|
||||
trackable_class = trackable_type.capitalize.constantize
|
||||
searchable_attr = (trackable_class.name == "Food") ? "long_desc" : "name"
|
||||
|
||||
[].tap do |array|
|
||||
if translation.present?
|
||||
array << find_duplicates(trackable_class, translation, searchable_attr)
|
||||
else
|
||||
trackable_class::Translation.find_each do |trackable_translation|
|
||||
translation_value = trackable_translation.send(searchable_attr.to_s)
|
||||
|
||||
duplicates = find_duplicates(trackable_class, translation_value, searchable_attr)
|
||||
array << duplicates if duplicates.present?
|
||||
end
|
||||
end
|
||||
|
||||
p "Same #{trackable_type.pluralize.camelize}: "
|
||||
p array.uniq
|
||||
end
|
||||
end
|
||||
|
||||
def find_duplicates(trackable_class, translation, searchable_attr = "name")
|
||||
escaped_translation = Regexp.escape(translation.squish).split(" ").join("s+")
|
||||
regex = "^\\s*#{escaped_translation}\\s*$"
|
||||
|
||||
same_translations = trackable_class::Translation.where("#{searchable_attr} ~* ?", regex)
|
||||
same_translations.map(&:"#{searchable_attr}") if same_translations.length > 1
|
||||
rescue ActiveRecord::StatementInvalid
|
||||
nil
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
class SwitchTrackableVisibility < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(trackable_usage_id)
|
||||
trackable_usage = TrackableUsage.find_by(id: trackable_usage_id)
|
||||
return unless trackable_usage
|
||||
|
||||
trackable = trackable_usage.trackable.reload
|
||||
|
||||
if trackable.trackable_usages_count >= Flaredown.config.trackables_min_popularity
|
||||
trackable.update(global: true)
|
||||
else
|
||||
trackable.update(global: false) if trackable.global? # rubocop:disable Style/IfInsideElse
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
class TopPostsMailerDispatcher
|
||||
include Sidekiq::Worker
|
||||
|
||||
TOP_POST_WEEKLY_TIME = ENV["TOP_POST_WEEKLY_TIME"]
|
||||
MAX_DIFF = 5.minutes
|
||||
|
||||
def perform
|
||||
return unless Time.current.tuesday? # Send on tuesdays
|
||||
|
||||
Profile.where(notify_top_posts: true, time_zone_name: find_time_zone_names).find_each(batch_size: 500) do |profile|
|
||||
GroupTopPostsJob.perform_async(profile.notify_token)
|
||||
end
|
||||
end
|
||||
|
||||
def find_time_zone_names
|
||||
Profile.pluck(:time_zone_name).compact.uniq.select do |time_zone_name|
|
||||
(Time.current.in_time_zone(time_zone_name) - TOP_POST_WEEKLY_TIME.in_time_zone(time_zone_name)).abs < MAX_DIFF
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
class UpdateCheckinReminders
|
||||
require "sidekiq/api"
|
||||
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(profile_id)
|
||||
profile = Profile.find_by(id: profile_id)
|
||||
|
||||
return unless profile
|
||||
return unless profile.checkin_reminder && profile.checkin_reminder_at
|
||||
|
||||
Sidekiq::ScheduledSet.new.find_job(profile.reminder_job_id)&.delete
|
||||
|
||||
job_id = CheckinReminderJob.perform_in(get_reminder_time(profile).minutes, profile_id, profile.checkin_reminder_at)
|
||||
profile.update_column(:reminder_job_id, job_id)
|
||||
end
|
||||
|
||||
def get_reminder_time(profile)
|
||||
time_zone_name = profile.time_zone_name
|
||||
checkin_at_timezone = profile.checkin_reminder_at.strftime("%H:%M").in_time_zone(time_zone_name)
|
||||
|
||||
# Select minutes
|
||||
(checkin_at_timezone - Time.current.in_time_zone(time_zone_name)).divmod(1.day)[1].divmod(1.minute)[0]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
class UpdatePostCountersJob
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(params)
|
||||
parent_type = params["parent_type"]
|
||||
return unless parent_type == "Post"
|
||||
|
||||
post = Post.find_by(_id: params["parent_id"], _type: parent_type)
|
||||
return unless post
|
||||
|
||||
post&.update_counters
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: Rails.application.secrets.smtp_email_from
|
||||
|
||||
REGEXP = /\A\s*([-\p{L}\d+._]{1,64})@((?:[-\p{L}\d]+\.)+\p{L}{2,})\s*\z/i
|
||||
|
||||
def valid_email?(email)
|
||||
email =~ REGEXP
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
class CheckinReminderMailer < ApplicationMailer
|
||||
layout "mailer_layout"
|
||||
|
||||
def remind(notification_hash)
|
||||
@email = notification_hash[:email]
|
||||
return unless valid_email?(@email)
|
||||
|
||||
user = User.find_by(email: @email)
|
||||
notify_token = user&.notify_token
|
||||
|
||||
return unless notify_token
|
||||
return if user&.rejected_type.present? # Rejected via AWS SES
|
||||
|
||||
@click_here_link = Rails.application.secrets.base_url
|
||||
@unsubscribe_link =
|
||||
Rails.application.secrets.base_url + "/unsubscribe/#{User.find_by(email: @email).notify_token}?stop_remind"
|
||||
attachments.inline["optional_email_img.png"] = File.read("public/images/optional_email_img.png")
|
||||
|
||||
mail(to: @email, subject: I18n.t("checkin_reminder_mailer.subject"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,23 @@
|
||||
class NotificationsMailer < ApplicationMailer
|
||||
layout "mailer_layout"
|
||||
|
||||
helper_method :resource_title, :resource_url
|
||||
|
||||
def notify(notification_hash)
|
||||
@email = notification_hash[:email]
|
||||
return unless valid_email?(@email)
|
||||
|
||||
@unsubscribe_link = Rails.application.secrets.base_url + "/unsubscribe/#{User.find_by(email: @email).notify_token}"
|
||||
@data = notification_hash[:data]
|
||||
|
||||
mail(to: @email, subject: "New response to your Flaredown message")
|
||||
end
|
||||
|
||||
def resource_title(object_id, class_name = "Post")
|
||||
class_name.constantize.find(object_id).title.to_s
|
||||
end
|
||||
|
||||
def resource_url(object_id, class_name = "Post")
|
||||
[ENV["BASE_URL"], class_name.tableize, object_id].map(&:to_s).join("/")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class PromotionRate::LowRateMailer < ApplicationMailer
|
||||
layout "mailer_layout"
|
||||
|
||||
def show(email, objects, start_date, end_date)
|
||||
@email = email
|
||||
@objects = objects
|
||||
@start_date = start_date
|
||||
@end_date = end_date
|
||||
|
||||
mail(to: @email, subject: I18n.t("promotion_rate.low_rate.subject"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
class PromotionRate::StatisticMailer < ApplicationMailer
|
||||
layout "mailer_layout"
|
||||
|
||||
def show(email, objects, start_date, end_date)
|
||||
@email = email
|
||||
@objects = objects
|
||||
@start_date = start_date
|
||||
@end_date = end_date
|
||||
|
||||
mail(to: @email, subject: I18n.t("promotion_rate.statistic.subject"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
class TopPostsMailer < ApplicationMailer
|
||||
layout "mailer_layout"
|
||||
|
||||
def notify(mailer_hash)
|
||||
@email = mailer_hash[:email]
|
||||
return unless valid_email?(@email)
|
||||
|
||||
@unsubscribe_link =
|
||||
Rails.application.secrets.base_url + "/unsubscribe/#{mailer_hash[:notify_token]}?notify_top_posts=false"
|
||||
@top_posts = Post.where(:_type => "Post", :_id.in => mailer_hash[:top_posts_ids])
|
||||
|
||||
mail(to: @email, subject: 'Weekly "top posts"')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
class UserDataMailer < ApplicationMailer
|
||||
def trackings_csv(email, csv_data)
|
||||
attachments["data.csv"] = csv_data
|
||||
|
||||
mail to: email
|
||||
end
|
||||
end
|
||||
100
worker-toolkit-flaredown/repo/backend/app/models/ability.rb
Normal file
100
worker-toolkit-flaredown/repo/backend/app/models/ability.rb
Normal file
@@ -0,0 +1,100 @@
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
||||
def initialize(user)
|
||||
user ||= User.new # guest user (not logged in)
|
||||
|
||||
can :manage, User, id: user.id
|
||||
can :manage, Profile, user_id: user.id
|
||||
|
||||
can :show, Condition
|
||||
can :index, Condition, global: true
|
||||
cannot :index, Condition, global: false
|
||||
can :index, Condition, global: false, id: user.trackings.where(trackable_type: "Condition").pluck(:trackable_id)
|
||||
can :read, Condition, global: false, id: popular_trackable_ids("Condition")
|
||||
can :create, Condition, global: false
|
||||
can :manage, Condition, id: user.condition_ids
|
||||
|
||||
can :show, Food
|
||||
can :index, Food, global: true
|
||||
cannot :index, Food, global: false
|
||||
can :index, Food, global: false, id: user.food_ids
|
||||
can :create, Food, global: false
|
||||
can :manage, Food, id: user.food_ids
|
||||
|
||||
can :read, HarveyBradshawIndex, encrypted_user_id: user.encrypted_id
|
||||
|
||||
can :create, HarveyBradshawIndex do |hbi|
|
||||
checkin = hbi.checkin
|
||||
|
||||
checkin.encrypted_user_id == user.encrypted_id && checkin.available_for_hbi?
|
||||
end
|
||||
|
||||
can :read, PromotionRate, encrypted_user_id: user.encrypted_id
|
||||
can [:create, :update], PromotionRate do |rate|
|
||||
checkin = rate.checkin
|
||||
|
||||
checkin.encrypted_user_id == user.encrypted_id && checkin.available_for_promotion?
|
||||
end
|
||||
|
||||
can [:create], Pattern
|
||||
can [:read, :update, :destroy], Pattern, encrypted_user_id: user.encrypted_id
|
||||
|
||||
can [:read], ChartsPattern
|
||||
|
||||
can :read, [Comment, Post]
|
||||
can :create, [Comment, Post], encrypted_user_id: user.encrypted_id
|
||||
|
||||
can :read, Notification
|
||||
can :destroy, Notification, encrypted_notify_user_id: user.encrypted_id
|
||||
can :update, Notification, encrypted_notify_user_id: user.encrypted_id
|
||||
|
||||
can :read, Postable, encrypted_user_id: user.encrypted_id
|
||||
|
||||
can :read, Reaction
|
||||
can [:create, :update, :destroy], Reaction, encrypted_user_id: user.encrypted_id
|
||||
|
||||
can :show, Symptom
|
||||
can :index, Symptom, global: true
|
||||
cannot :index, Symptom, global: false
|
||||
can :index, Symptom, global: false, id: user.trackings.where(trackable_type: "Symptom").pluck(:trackable_id)
|
||||
can :read, Symptom, global: false, id: popular_trackable_ids("Symptom")
|
||||
can :create, Symptom, global: false
|
||||
can :manage, Symptom, id: user.symptom_ids
|
||||
|
||||
can [:read, :update], TopicFollowing, encrypted_user_id: user.encrypted_id
|
||||
|
||||
can :show, Treatment
|
||||
can :index, Treatment, global: true
|
||||
cannot :index, Treatment, global: false
|
||||
can :index, Treatment, global: false, id: user.trackings.where(trackable_type: "Treatment").pluck(:trackable_id)
|
||||
can :read, Symptom, global: false, id: popular_trackable_ids("Treatment")
|
||||
can :create, Treatment, global: false
|
||||
can :manage, Treatment, id: user.treatment_ids
|
||||
|
||||
can :manage, Tracking, user_id: user.id
|
||||
|
||||
can :show, Tag
|
||||
can :index, Tag, global: true
|
||||
cannot :index, Tag, global: false
|
||||
can :index, Tag, global: false, id: user.tag_ids
|
||||
can :create, Tag, global: false
|
||||
can :manage, Tag, id: user.tag_ids
|
||||
|
||||
can :read, Weather if user.persisted?
|
||||
can :read, NilClass if user.persisted?
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
||||
|
||||
private
|
||||
|
||||
def popular_trackable_ids(trackable_class_name)
|
||||
user_trackable_class = "User#{trackable_class_name}".constantize
|
||||
trackable_id_attr = "#{trackable_class_name.underscore}_id".to_sym
|
||||
counts = user_trackable_class.group(trackable_id_attr).count
|
||||
|
||||
min_popularity = Flaredown.config.trackables_min_popularity
|
||||
counts.select { |_id, count| count >= min_popularity }.keys
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
||||
38
worker-toolkit-flaredown/repo/backend/app/models/chart.rb
Normal file
38
worker-toolkit-flaredown/repo/backend/app/models/chart.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
class Chart
|
||||
include ActiveModel::Serialization
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :id, :user, :start_at, :end_at, :includes
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :user, :start_at, :end_at, presence: true
|
||||
|
||||
def checkins
|
||||
@checkins ||= user.checkins.by_date(start_at.to_date, end_at.to_date).map do |checkin|
|
||||
checkin.includes = includes
|
||||
|
||||
checkin
|
||||
end
|
||||
end
|
||||
|
||||
def trackables
|
||||
@_trackables ||= trackings.map(&:trackable)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def trackings
|
||||
return Tracking.none if includes.blank?
|
||||
|
||||
relation = Tracking.includes(:trackable)
|
||||
|
||||
relation
|
||||
.where(trackable_type: :Condition, trackable_id: includes[:conditions] || [])
|
||||
.or(relation.where(trackable_type: :Symptom, trackable_id: includes[:symptoms] || []))
|
||||
.or(relation.where(trackable_type: :Treatment, trackable_id: includes[:treatments] || []))
|
||||
.where(user_id: user.id)
|
||||
.active_in_range(start_at.to_date, end_at.to_date)
|
||||
end
|
||||
end
|
||||
165
worker-toolkit-flaredown/repo/backend/app/models/checkin.rb
Normal file
165
worker-toolkit-flaredown/repo/backend/app/models/checkin.rb
Normal file
@@ -0,0 +1,165 @@
|
||||
class Checkin
|
||||
include Mongoid::Document
|
||||
|
||||
HBI_PERIODICITY = 7
|
||||
PR_PERIODICITY = 7
|
||||
PR_START_FROM = 7
|
||||
|
||||
attr_accessor :includes
|
||||
|
||||
FIELD_TYPE = %w[tag food].freeze
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
field :date, type: DateTime
|
||||
field :food_ids, type: Array, default: []
|
||||
field :note, type: String
|
||||
field :postal_code, type: String
|
||||
field :tag_ids, type: Array
|
||||
field :weather_id, type: Integer
|
||||
field :encrypted_user_id, type: String, encrypted: {type: :integer}
|
||||
field :position_id, type: Integer
|
||||
field :promotion_skipped_at, type: Date
|
||||
|
||||
#
|
||||
# Relations
|
||||
#
|
||||
has_one :harvey_bradshaw_index
|
||||
has_one :promotion_rate, dependent: :destroy
|
||||
has_many :treatments, class_name: "Checkin::Treatment"
|
||||
has_many :conditions, class_name: "Checkin::Condition"
|
||||
has_many :symptoms, class_name: "Checkin::Symptom"
|
||||
accepts_nested_attributes_for :conditions, :symptoms, :treatments, allow_destroy: true
|
||||
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
index(encrypted_user_id: 1)
|
||||
index(date: 1, encrypted_user_id: 1)
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :encrypted_user_id, presence: true
|
||||
validates :date, presence: true
|
||||
|
||||
#
|
||||
# Scopes
|
||||
#
|
||||
scope :by_date, ->(startkey, endkey) { where(:date.gte => startkey, :date.lte => endkey) }
|
||||
|
||||
def user
|
||||
@user ||= User.find(user_id)
|
||||
end
|
||||
|
||||
def weather
|
||||
@weather ||= Weather.find_by(id: weather_id)
|
||||
end
|
||||
|
||||
def position
|
||||
@position ||= Position.find_by(id: position_id)
|
||||
end
|
||||
|
||||
def tags
|
||||
if includes
|
||||
@_tags_included ||= Tag.where(id: tag_ids - (includes[:tags] || []))
|
||||
else
|
||||
@_tags ||= Tag.where(id: tag_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def foods
|
||||
if includes
|
||||
@_foods_included ||= Food.where(id: food_ids - (includes[:foods] || []))
|
||||
else
|
||||
@_foods ||= Food.where(id: food_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def available_for_hbi?
|
||||
return true if harvey_bradshaw_index
|
||||
return true unless latest_hbi
|
||||
|
||||
HBI_PERIODICITY - ((latest_hbi.date)...date).count < 1
|
||||
end
|
||||
|
||||
def available_for_promotion?
|
||||
return true if promotion_rate
|
||||
return false if user_has_already_rated?
|
||||
return false unless date.today?
|
||||
return ready_for_promotion? if latest_skipped_pr_at.blank?
|
||||
|
||||
PR_PERIODICITY - ((latest_skipped_pr_at)...date).count < 1
|
||||
end
|
||||
|
||||
class Condition
|
||||
include Mongoid::Document
|
||||
include Checkin::Trackable
|
||||
include Checkin::Fiveable
|
||||
|
||||
field :condition_id, type: Integer
|
||||
|
||||
validates :condition_id, uniqueness: {scope: :checkin_id}
|
||||
end
|
||||
|
||||
class Symptom
|
||||
include Mongoid::Document
|
||||
include Checkin::Trackable
|
||||
include Checkin::Fiveable
|
||||
|
||||
field :symptom_id, type: Integer
|
||||
|
||||
validates :symptom_id, uniqueness: {scope: :checkin_id}
|
||||
end
|
||||
|
||||
class Treatment
|
||||
include Mongoid::Document
|
||||
include Checkin::Trackable
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
field :treatment_id, type: Integer
|
||||
field :value, type: String
|
||||
field :is_taken, type: Boolean
|
||||
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
index(treatment_id: 1)
|
||||
index(treatment_id: 1, is_taken: 1, value: 1)
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :treatment_id, uniqueness: {scope: :checkin_id}
|
||||
end
|
||||
|
||||
def self.ids_by_category_attrs(category_name, trackable_id)
|
||||
where(id: {"$in" => "Checkin::#{category_name.camelize}".constantize
|
||||
.where(:"#{category_name}_id" => trackable_id, :value.ne => nil).pluck(:checkin_id)})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def latest_hbi
|
||||
@_latest_hbi ||= HarveyBradshawIndex.where(encrypted_user_id: encrypted_user_id).order(date: :desc).first
|
||||
end
|
||||
|
||||
def latest_skipped_pr_at
|
||||
@_lates_skipped_pr ||=
|
||||
Checkin
|
||||
.where(encrypted_user_id: encrypted_user_id)
|
||||
.order_by(promotion_skipped_at: :desc)
|
||||
.first&.promotion_skipped_at
|
||||
end
|
||||
|
||||
def ready_for_promotion?
|
||||
user.created_at <= PR_START_FROM.day.ago
|
||||
end
|
||||
|
||||
def user_has_already_rated?
|
||||
@_rated ||= PromotionRate.where(encrypted_user_id: encrypted_user_id).any?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
module Checkin::Fiveable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
field :value, type: Integer
|
||||
validates :value, inclusion: {in: (0..4), allow_blank: true}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module Checkin::Trackable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
field :position, type: Integer
|
||||
field :color_id, type: String
|
||||
|
||||
#
|
||||
# Relations
|
||||
#
|
||||
belongs_to :checkin, index: true
|
||||
end
|
||||
end
|
||||
20
worker-toolkit-flaredown/repo/backend/app/models/comment.rb
Normal file
20
worker-toolkit-flaredown/repo/backend/app/models/comment.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
class Comment
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
include Usernameable
|
||||
|
||||
store_in collection: "postables"
|
||||
|
||||
field :body, type: String
|
||||
field :_type, type: String, default: -> { self.class.name }
|
||||
field :encrypted_user_id, type: String, encrypted: {type: :integer}
|
||||
|
||||
validates :body, :post, :encrypted_user_id, presence: true
|
||||
|
||||
belongs_to :post, index: true, counter_cache: true
|
||||
|
||||
has_many :reactions, as: :reactable, dependent: :destroy
|
||||
has_many :notifications, as: :notificateable, dependent: :destroy
|
||||
|
||||
after_create { post.update(last_commented: Time.current) }
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
module Authenticatable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
#
|
||||
# Devise
|
||||
#
|
||||
devise :database_authenticatable,
|
||||
:rememberable,
|
||||
:recoverable,
|
||||
:trackable,
|
||||
:validatable,
|
||||
:invitable,
|
||||
:omniauthable
|
||||
|
||||
#
|
||||
# Validates
|
||||
#
|
||||
validates :password, confirmation: true
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
module Topicable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
TOPIC_TYPES = %w[tag symptom condition treatment].freeze
|
||||
|
||||
included do
|
||||
field :tag_ids, type: Array, default: []
|
||||
field :symptom_ids, type: Array, default: []
|
||||
field :condition_ids, type: Array, default: []
|
||||
field :treatment_ids, type: Array, default: []
|
||||
|
||||
field :encrypted_user_id, type: String, encrypted: {type: :integer}
|
||||
|
||||
validates :encrypted_user_id, presence: true
|
||||
|
||||
TOPIC_TYPES.each do |relative|
|
||||
pluralized_relative = relative.pluralize
|
||||
|
||||
define_method(:"#{pluralized_relative}") do
|
||||
ivar_name = :"@_#{pluralized_relative}"
|
||||
|
||||
value = instance_variable_get(ivar_name)
|
||||
|
||||
return value if value.present?
|
||||
|
||||
value = instance_variable_set(ivar_name, relative.classify.constantize.where(id: send("#{relative}_ids")))
|
||||
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
module Usernameable
|
||||
def user_name
|
||||
Profile.select(:screen_name).find_by!(user_id: SymmetricEncryption.decrypt(encrypted_user_id)).screen_name
|
||||
rescue SymmetricEncryption::CipherError
|
||||
""
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: conditions
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# global :boolean default(TRUE)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# trackable_usages_count :integer default(0)
|
||||
#
|
||||
|
||||
class Condition < ActiveRecord::Base
|
||||
has_many :condition_translations, class_name: "Condition::Translation"
|
||||
|
||||
#
|
||||
# Localized attributes
|
||||
#
|
||||
translates :name
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
class DayHabit < RankedEnum
|
||||
class << self
|
||||
def all_ids
|
||||
%w[lying_down sitting_down on_feet driving working_with_hands]
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
I18n.t "#{key}.#{id}.name"
|
||||
end
|
||||
|
||||
def description
|
||||
I18n.t "#{key}.#{id}.description"
|
||||
end
|
||||
end
|
||||
10
worker-toolkit-flaredown/repo/backend/app/models/dose.rb
Normal file
10
worker-toolkit-flaredown/repo/backend/app/models/dose.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class Dose
|
||||
include ActiveModel::Serialization
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :name
|
||||
|
||||
def id
|
||||
name.parameterize
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
class EducationLevel < RankedEnum
|
||||
class << self
|
||||
def all_ids
|
||||
%w[less_than_hi_school hi_school college_degree bachelors_degree advanced_degree]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
class Ethnicity < RankedEnum
|
||||
class << self
|
||||
def all_ids
|
||||
%w[ latino white east_asian south_asian black oceanian
|
||||
middle_eastern native_american african other not_sure ]
|
||||
end
|
||||
end
|
||||
end
|
||||
20
worker-toolkit-flaredown/repo/backend/app/models/feedback.rb
Normal file
20
worker-toolkit-flaredown/repo/backend/app/models/feedback.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
class Feedback
|
||||
include Mongoid::Document
|
||||
include Mongoid::Timestamps
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
field :email, type: String
|
||||
field :delete_reason, type: String
|
||||
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
index(date: 1, encrypted_user_id: 1)
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :email, :delete_reason, presence: true
|
||||
end
|
||||
54
worker-toolkit-flaredown/repo/backend/app/models/food.rb
Normal file
54
worker-toolkit-flaredown/repo/backend/app/models/food.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
class Food < ActiveRecord::Base
|
||||
LANG_MAP = {
|
||||
en: :english,
|
||||
it: :italian
|
||||
}.freeze
|
||||
|
||||
has_many :food_translations, class_name: "Food::Translation"
|
||||
|
||||
translates :long_desc, :shrt_desc, :comname, :sciname
|
||||
|
||||
alias_method :name, :long_desc
|
||||
|
||||
class << self
|
||||
def fts(query, limit, user_id)
|
||||
find_by_sql(
|
||||
[
|
||||
fts_sql,
|
||||
{
|
||||
user_id: user_id,
|
||||
lang: LANG_MAP[I18n.locale] || :simple,
|
||||
query: query.strip.split(/(\s*,\s*)|\s+/).map { |s| "#{s}:*" }.join("&"),
|
||||
limit: limit,
|
||||
locale: I18n.locale
|
||||
}
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fts_sql
|
||||
<<-SQL.strip_heredoc
|
||||
SELECT id, long_desc, searchable, global
|
||||
FROM (
|
||||
SELECT
|
||||
to_tsvector(:lang, ft.long_desc) AS searchable,
|
||||
foods.id AS id,
|
||||
foods.global AS global,
|
||||
ft.long_desc AS long_desc
|
||||
FROM foods
|
||||
INNER JOIN food_translations AS ft ON ft.food_id = foods.id
|
||||
WHERE ft.locale = :locale AND foods.id IN (
|
||||
SELECT uf.food_id
|
||||
FROM user_foods AS uf
|
||||
WHERE uf.user_id = :user_id
|
||||
) OR foods.global IS TRUE
|
||||
) f
|
||||
WHERE f.searchable @@ to_tsquery(:lang, :query)
|
||||
ORDER BY ts_rank_cd(f.searchable, to_tsquery(:lang, :query), 1) DESC
|
||||
LIMIT :limit
|
||||
SQL
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,69 @@
|
||||
class HarveyBradshawIndex
|
||||
include Mongoid::Document
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
field :date, type: Date
|
||||
|
||||
field :score, type: Integer
|
||||
field :stools, type: Integer
|
||||
field :well_being, type: Integer
|
||||
field :abdominal_mass, type: Integer
|
||||
field :abdominal_pain, type: Integer
|
||||
|
||||
field :abscess, type: Boolean
|
||||
field :uveitis, type: Boolean
|
||||
field :arthralgia, type: Boolean
|
||||
field :new_fistula, type: Boolean
|
||||
field :anal_fissure, type: Boolean
|
||||
field :aphthous_ulcers, type: Boolean
|
||||
field :erythema_nodosum, type: Boolean
|
||||
field :pyoderma_gangrenosum, type: Boolean
|
||||
|
||||
field :encrypted_user_id, type: String, encrypted: {type: :integer}
|
||||
|
||||
#
|
||||
# Relations
|
||||
#
|
||||
belongs_to :checkin, index: true
|
||||
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
index(date: 1, encrypted_user_id: 1)
|
||||
|
||||
#
|
||||
# Validations
|
||||
#
|
||||
validates :checkin, :stools, :well_being, :abdominal_mass, :abdominal_pain, presence: true
|
||||
validates :checkin_id, uniqueness: true
|
||||
|
||||
#
|
||||
# Callbacks
|
||||
#
|
||||
before_save :calculate_score
|
||||
before_create :set_date_and_user_id
|
||||
|
||||
private
|
||||
|
||||
def set_date_and_user_id
|
||||
self.date = checkin.date
|
||||
self.encrypted_user_id = checkin.encrypted_user_id
|
||||
end
|
||||
|
||||
def calculate_score
|
||||
self.score = stools + well_being + abdominal_mass + abdominal_pain
|
||||
|
||||
self.score += [
|
||||
abscess,
|
||||
uveitis,
|
||||
arthralgia,
|
||||
new_fistula,
|
||||
anal_fissure,
|
||||
aphthous_ulcers,
|
||||
erythema_nodosum,
|
||||
pyoderma_gangrenosum
|
||||
].count { |v| v }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
class Invitation
|
||||
include ActiveModel::Serialization
|
||||
|
||||
attr_reader :id, :email, :user
|
||||
|
||||
def initialize(id, user)
|
||||
@id = id
|
||||
@user = user
|
||||
@email = user.email
|
||||
end
|
||||
|
||||
def accept!(params = {})
|
||||
User.accept_invitation!({invitation_token: id}.merge(params))
|
||||
end
|
||||
|
||||
def self.find(id)
|
||||
# FIXME
|
||||
# rubocop:disable Rails/DynamicFindBy
|
||||
user = User.find_by_invitation_token(id, true)
|
||||
# rubocop:enable Rails/DynamicFindBy
|
||||
# FIXME
|
||||
# rubocop:disable Style/SignalException
|
||||
fail ActiveRecord::RecordNotFound if user.nil?
|
||||
# rubocop:enable Style/SignalException
|
||||
new(id, user)
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user