#2467 pending deeper testing but initially user defined limits (And twitter credits one) in place

pull/2528/head
Kartones 10 years ago
parent fe6d878064
commit a9dadd9911

@ -1,4 +1,8 @@
3.8.1 (2015-02-xx)
3.9.0 (2015-02-xx)
------------------
3.8.1 (2015-02-26)
------------------
* Added config for basemaps [#1954], see
[documentation](https://github.com/CartoDB/cartodb/wiki/How-to-configure-basemaps-in-CartoDB)
@ -31,7 +35,7 @@
* Change visuals in share view of privacy dialog [#2492](https://github.com/CartoDB/cartodb/pull/2492)
* Added back Twitter import for new create dialog.
* Improved speed in dashboard caching frontend side [#2465](https://github.com/CartoDB/cartodb/pull/2465)
* Add user_defined_limits to DataImport, and the feature of imports to support certain user defined limits. Currently only used for `twitter_credits_limit` at importer create endpoint.
Bugfixes:
* When being in any configuration page remove the arrow from the breadcrumb [#2312](https://github.com/CartoDB/cartodb/pull/2312)
* Pressing enter when deleting a table opens a new modal [#2126](https://github.com/CartoDB/cartodb/pull/2126)

@ -57,29 +57,29 @@ class Api::Json::ImportsController < Api::ApplicationController
options = default_creation_options
if params.fetch(:url).present?
if params[:url].present?
options.merge!({
data_source: params.fetch(:url).presence
data_source: params.fetch(:url)
})
elsif params[:remote_visualization_id].present?
external_source = external_source(params[:remote_visualization_id])
options.merge!({
content_guessing: false,
type_guessing: true,
quoted_fields_guessing: true,
data_source: external_source.import_url.presence,
create_visualization: false
content_guessing: false,
type_guessing: true,
quoted_fields_guessing: true,
data_source: external_source.import_url.presence,
create_visualization: false
})
else
results = upload_file_to_storage(params, request, Cartodb.config[:importer]['s3'])
options.merge!({
data_source: results[:file_uri].presence,
# Not queued import is set by skipping pending state and setting directly as already enqueued
state: results[:enqueue] ? DataImport::STATE_PENDING : DataImport::STATE_ENQUEUED
data_source: results[:file_uri].presence,
# Not queued import is set by skipping pending state and setting directly as already enqueued
state: results[:enqueue] ? DataImport::STATE_PENDING : DataImport::STATE_ENQUEUED
})
end
data_import = DataImport.create(options)
data_import = DataImport.create(options.merge!({ user_defined_limits: ::JSON.dump(options[:user_defined_limits]) }))
ExternalDataImport.new(data_import.id, external_source.id).save if external_source.present?
@ -271,6 +271,11 @@ class Api::Json::ImportsController < Api::ApplicationController
private
def default_creation_options
user_defined_limits = params.fetch(:user_defined_limits, {})
# Sanitize
user_defined_limits[:twitter_credits_limit] =
user_defined_limits[:twitter_credits_limit].presence.nil? ? 0 : user_defined_limits[:twitter_credits_limit].to_i
{
user_id: current_user.id,
table_name: params[:table_name].presence,
@ -287,8 +292,10 @@ class Api::Json::ImportsController < Api::ApplicationController
content_guessing: ["true", true].include?(params[:content_guessing]),
state: DataImport::STATE_PENDING, # Pending == enqueue the task
upload_host: Socket.gethostname,
create_visualization: ["true", true].include?(params[:create_vis])
create_visualization: ["true", true].include?(params[:create_vis]),
user_defined_limits: user_defined_limits
}
end
def decorate_twitter_import_data!(data, data_import)
return if data_import.service_name != CartoDB::Datasources::Search::Twitter::DATASOURCE_NAME

@ -30,30 +30,36 @@ class DataImport < Sequel::Model
attr_accessor :log, :results
# @see store_results() method also when adding new fields
PUBLIC_ATTRIBUTES = %W{
id
user_id
table_id
data_type
table_name
state
error_code
queue_id
get_error_text
tables_created_count
synchronization_id
service_name
service_item_id
type_guessing
quoted_fields_guessing
content_guessing
server
host
upload_host
resque_ppid
create_visualization
visualization_id
}
PUBLIC_ATTRIBUTES = [
'id',
'user_id',
'table_id',
'data_type',
'table_name',
'state',
'error_code',
'queue_id',
'get_error_text',
'tables_created_count',
'synchronization_id',
'service_name',
'service_item_id',
'type_guessing',
'quoted_fields_guessing',
'content_guessing',
'server',
'host',
'upload_host',
'resque_ppid',
'create_visualization',
'visualization_id',
# String field containing a json, format:
# {
# twitter_credits: Integer
# }
# No automatic conversion coded
'user_defined_limits'
]
# Not all constants are used, but so that we keep track of available states
STATE_ENQUEUED = 'enqueued' # Default state for imports whose files are not yet at "import source"
@ -687,7 +693,10 @@ class DataImport < Sequel::Model
oauth = current_user.oauths.select(datasource_name)
# Tables metadata DB also store resque data
datasource = DatasourcesFactory.get_datasource(
datasource_name, current_user, { redis_storage: $tables_metadata })
datasource_name, current_user, {
redis_storage: $tables_metadata,
user_defined_limits: ::JSON.parse(user_defined_limits).symbolize_keys
})
datasource.report_component = Rollbar
datasource.token = oauth.token unless oauth.nil?
rescue => ex

@ -0,0 +1,9 @@
Sequel.migration do
up do
add_column :data_imports, :user_defined_limits, :text, default: '{}'
end
down do
drop_column :data_imports, :user_defined_limits
end
end

@ -39,8 +39,8 @@ module CartoDB
when Url::MailChimp::DATASOURCE_NAME
Url::MailChimp.get_new(DatasourcesFactory.config_for(datasource_name, user), user)
when Search::Twitter::DATASOURCE_NAME
Search::Twitter.get_new(DatasourcesFactory.config_for(datasource_name, user),
user, additional_config[:redis_storage])
Search::Twitter.get_new(DatasourcesFactory.config_for(datasource_name, user), user,
additional_config[:redis_storage], additional_config[:user_defined_limits])
when nil
nil
else

@ -31,6 +31,8 @@ module CartoDB
FILTER_CATEGORIES = :categories
FILTER_TOTAL_RESULTS = :totalResults
USER_LIMITS_FILTER_CREDITS = :twitter_credits
CATEGORY_NAME_KEY = :name
CATEGORY_TERMS_KEY = :terms
@ -58,8 +60,9 @@ module CartoDB
# ]
# @param user User
# @param redis_storage Redis|nil (optional)
# @param user_defined_limits Hash|nil (optional)
# @throws UninitializedError
def initialize(config, user, redis_storage = nil)
def initialize(config, user, redis_storage = nil, user_defined_limits={})
@service_name = DATASOURCE_NAME
@filters = Hash.new
@ -69,6 +72,9 @@ module CartoDB
raise MissingConfigurationError.new('missing password', DATASOURCE_NAME) unless config.include?('password')
raise MissingConfigurationError.new('missing search_url', DATASOURCE_NAME) unless config.include?('search_url')
@user_defined_limits = user_defined_limits
@search_api_config = {
TwitterSearch::SearchAPI::CONFIG_AUTH_REQUIRED => config['auth_required'],
TwitterSearch::SearchAPI::CONFIG_AUTH_USERNAME => config['username'],
@ -98,9 +104,10 @@ module CartoDB
# @param config {}
# @param user User
# @param redis_storage Redis|nil
# @param user_defined_limits Hash|nil
# @return CartoDB::Datasources::Search::TwitterSearch
def self.get_new(config, user, redis_storage = nil)
return new(config, user, redis_storage)
def self.get_new(config, user, redis_storage = nil, user_defined_limits={})
return new(config, user, redis_storage, user_defined_limits)
end
# If will provide a url to download the resource, or requires calling get_resource()
@ -236,6 +243,19 @@ module CartoDB
attr_accessor :search_api_config, :csv_dumper
attr_reader :data_import_item
# Returns if the user set a maximum credits to use
# @return Integer
def twitter_credit_limits
@user_defined_limits.fetch(USER_LIMITS_FILTER_CREDITS, 0)
end
# Wraps check of specified user limit or not (to use instead his max quota)
# @return Integer
def remaining_quota
twitter_credit_limits > 0 ? [@user.remaining_twitter_quota, twitter_credit_limits].min
: @user.remaining_twitter_quota
end
def table_name
terms_fragment = @filters[FILTER_CATEGORIES].map { |category|
clean_category(category[CATEGORY_TERMS_KEY]).gsub(/[^0-9a-z,]/i, '').gsub(/[,]/i, '_')
@ -318,10 +338,11 @@ module CartoDB
log("Temp files:\n#{@csv_dumper.file_paths}")
log("#{@csv_dumper.original_file_paths}\n#{@csv_dumper.headers_path}")
# Make sure we don't charge extra tweets if the user cannot go overquota
# (even if we "lose" charging a block or two of tweets)
if !@user.soft_twitter_datasource_limit && (@user.remaining_twitter_quota - @used_quota) < 0
@used_quota = @user.remaining_twitter_quota
if twitter_credit_limits > 0 || !@user.soft_twitter_datasource_limit
if (remaining_quota - @used_quota) < 0
# Make sure we don't charge extra tweets (even if we "lose" charging a block or two of tweets)
@used_quota = remaining_quota
end
end
# remaining quota is calc. on the fly based on audits/imports
@ -342,9 +363,12 @@ module CartoDB
out_of_quota = false
@user_semaphore.synchronize {
if !@user.soft_twitter_datasource_limit && (@user.remaining_twitter_quota - @used_quota) <= 0
out_of_quota = true
next_results_cursor = nil
# Credit limits must be honoured above soft limit
if twitter_credit_limits > 0 || !@user.soft_twitter_datasource_limit
if remaining_quota - @used_quota <= 0
out_of_quota = true
next_results_cursor = nil
end
end
}
@ -490,25 +514,34 @@ module CartoDB
}.compact
end
# Max results per page
# @param user User
def build_maxresults_field(user)
# user about to hit quota?
if user.remaining_twitter_quota < TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
if user.soft_twitter_datasource_limit
# But can go beyond limits
TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
if twitter_credit_limits > 0
[remaining_quota, TwitterSearch::SearchAPI::MAX_PAGE_RESULTS].min
else
# user about to hit quota?
if remaining_quota < TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
if user.soft_twitter_datasource_limit
# But can go beyond limits
TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
else
remaining_quota
end
else
user.remaining_twitter_quota
TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
end
else
TwitterSearch::SearchAPI::MAX_PAGE_RESULTS
end
end
# Max total results
# @param user User
def build_total_results_field(user)
if user.soft_twitter_datasource_limit
if twitter_credit_limits == 0 && user.soft_twitter_datasource_limit
NO_TOTAL_RESULTS
else
user.remaining_twitter_quota
remaining_quota
end
end
@ -530,6 +563,7 @@ module CartoDB
# @param user User
# @return boolean
def has_enough_quota?(user)
# As this is used to disallow searches (and throw exceptions) don't use here user limits
user.soft_twitter_datasource_limit || (user.remaining_twitter_quota > 0)
end

@ -91,6 +91,10 @@ module CartoDB
false
end
def set_limits(limits={})
# not supported
end
attr_reader :source_file, :datasource, :etag, :last_modified
attr_accessor :url

Loading…
Cancel
Save