188 lines
6.2 KiB
Ruby
188 lines
6.2 KiB
Ruby
|
require 'active_record'
|
||
|
require 'cartodb-common'
|
||
|
require_dependency 'carto/db/connection'
|
||
|
|
||
|
module Carto
|
||
|
class UserService
|
||
|
|
||
|
def initialize(user_model)
|
||
|
@user = user_model
|
||
|
end
|
||
|
|
||
|
# TODO: Review usage to move to UserPresenter if not used anywhere else
|
||
|
|
||
|
# Only returns owned tables (not shared ones)
|
||
|
def table_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.new
|
||
|
.with_user_id(@user.id)
|
||
|
.with_type(Carto::Visualization::TYPE_CANONICAL)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def owned_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.new
|
||
|
.with_user_id(@user.id)
|
||
|
.with_type(Carto::Visualization::TYPE_DERIVED)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.new
|
||
|
.with_owned_by_or_shared_with_user_id(@user.id)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def public_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.user_public_visualizations(@user)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def public_privacy_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.user_public_privacy_visualizations(@user)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def link_privacy_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.user_link_privacy_visualizations(@user)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def password_privacy_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.user_password_privacy_visualizations(@user)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def all_visualization_count
|
||
|
return 0 unless @user.id
|
||
|
|
||
|
Carto::VisualizationQueryBuilder.user_all_visualizations(@user)
|
||
|
.build
|
||
|
.count
|
||
|
end
|
||
|
|
||
|
def twitter_imports_count(options={})
|
||
|
date_to = (options[:to] ? options[:to].to_date : Date.today)
|
||
|
date_from = (options[:from] ? options[:from].to_date : @user.last_billing_cycle)
|
||
|
Carto::SearchTweet.twitter_imports_count(@user.search_tweets, date_from, date_to)
|
||
|
end
|
||
|
|
||
|
# Returns an array representing the last 30 days, populated with api_calls
|
||
|
# from three different sources
|
||
|
def get_api_calls(options = {})
|
||
|
return CartoDB::Stats::APICalls.new.get_api_calls_without_dates(@user.username, {old_api_calls: false})
|
||
|
end
|
||
|
|
||
|
# This method is innaccurate and understates point based tables (the /2 is to account for the_geom_webmercator)
|
||
|
# TODO: Without a full table scan, ignoring the_geom_webmercator, we cannot accuratly asses table size
|
||
|
# Needs to go on a background job.
|
||
|
def db_size_in_bytes
|
||
|
return 0 if @user.new_record?
|
||
|
|
||
|
attempts = 0
|
||
|
begin
|
||
|
# Hack to support users without the new MU functiones loaded
|
||
|
# TODO: Check this works as expected
|
||
|
user_data_size_function = cartodb_extension_version_pre_mu? ?
|
||
|
"CDB_UserDataSize()" :
|
||
|
"CDB_UserDataSize('#{@user.database_schema}')"
|
||
|
in_database(as: :superuser) do |user_database|
|
||
|
user_database.transaction do
|
||
|
user_database.execute(%{SET LOCAL lock_timeout = '1s'})
|
||
|
user_database.execute(%{SELECT cartodb.#{user_data_size_function}}).first['cdb_userdatasize'].to_i
|
||
|
end
|
||
|
end
|
||
|
rescue => e
|
||
|
attempts += 1
|
||
|
begin
|
||
|
in_database(as: :superuser).execute("ANALYZE")
|
||
|
rescue => ee
|
||
|
CartoDB.report_exception(ee, "Failed to get user db size, retrying...", user: @user)
|
||
|
raise ee
|
||
|
end
|
||
|
retry unless attempts > 1
|
||
|
CartoDB.notify_exception(e, user: @user)
|
||
|
# INFO: we need to return something to avoid 'disabled' return value
|
||
|
nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def database_username
|
||
|
if Rails.env.production?
|
||
|
"cartodb_user_#{@user.id}"
|
||
|
elsif Rails.env.staging?
|
||
|
"cartodb_staging_user_#{@user.id}"
|
||
|
else
|
||
|
"#{Rails.env}_cartodb_user_#{@user.id}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def cartodb_extension_version_pre_mu?
|
||
|
current_version = cartodb_extension_semver(cartodb_extension_version)
|
||
|
if current_version.size == 3
|
||
|
major, minor, _ = current_version
|
||
|
major == 0 and minor < 3
|
||
|
else
|
||
|
raise 'Current cartodb extension version does not match standard x.y.z format'
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def database_public_username
|
||
|
@user.database_schema != CartoDB::DEFAULT_DB_SCHEMA ? "cartodb_publicuser_#{@user.id}" : CartoDB::PUBLIC_DB_USER
|
||
|
end
|
||
|
|
||
|
def organization_member_group_role_member_name
|
||
|
@user.in_database.execute(
|
||
|
"SELECT cartodb.CDB_Organization_Member_Group_Role_Member_Name() as org_member_role;"
|
||
|
).first['org_member_role']
|
||
|
end
|
||
|
|
||
|
# Returns a tree elements array with [major, minor, patch] as in http://semver.org/
|
||
|
def cartodb_extension_semver(extension_version)
|
||
|
extension_version.split('.').take(3).map(&:to_i)
|
||
|
end
|
||
|
|
||
|
def cartodb_extension_version
|
||
|
@cartodb_extension_version ||= in_database(:as => :superuser).execute('select cartodb.cdb_version() as v')
|
||
|
.first['v']
|
||
|
end
|
||
|
|
||
|
def database_password
|
||
|
Carto::Common::EncryptionService.hex_digest(@user.crypted_password) + database_username
|
||
|
end
|
||
|
|
||
|
def in_database(options = {})
|
||
|
options[:username] = @user.database_username
|
||
|
options[:password] = @user.database_password
|
||
|
options[:user_schema] = @user.database_schema
|
||
|
Carto::Db::Connection.connect(@user.database_host, @user.database_name, options) do |_, connection|
|
||
|
if block_given?
|
||
|
yield(connection)
|
||
|
else
|
||
|
connection
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|