lib/gooddata/models/metadata/report_definition.rb
# encoding: UTF-8
#
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
require_relative '../metadata'
require_relative 'metadata'
# GoodData Module
module GoodData
# Report Definition
# TODO: Add more doc ...
class ReportDefinition < GoodData::MdObject
class << self
# Method intended to get all objects of that type in a specified project
#
# @param options [Hash] the options hash
# @option options [Boolean] :full if passed true the subclass can decide
# to pull in full objects. This is desirable from the usability POV
# but unfortunately has negative impact on performance so it is not
# the default.
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
def all(options = { :client => GoodData.connection, :project => GoodData.project })
query('reportDefinition', ReportDefinition, options)
end
def create_metrics_part(left, top)
stuff = Array(left) + Array(top)
stuff.select { |item| item.respond_to?(:metric?) && item.metric? }.map do |metric|
create_metric_part(metric)
end
end
alias_method :create_measures_part, :create_metrics_part
def create_metric_part(metric)
{
'alias' => metric.title,
'uri' => metric.uri
}
end
alias_method :create_measure_part, :create_metric_part
def create_attribute_part(attrib)
{
'attribute' => {
'alias' => '',
'totals' => [],
'uri' => attrib.uri
}
}
end
# Method creates the list of filter representaion suitable for posting on
# the api. It can currently recognize 2 types of filters. Variable filters
# and attribute filters. Method for internal usage.
#
# @param filters [GoodData::Variable|Array<Array>]
# @param options [Hash] the options hash
# @return [Array<Hash>] Returns the structure that is stored internally in the report definition and later psted on the API
def create_filters_part(filters, options = {})
project = options[:project]
vars = filters.select { |f| f.is_a?(GoodData::Variable) }.map { |v| { expression: "[#{v.uri}]" } }
category = filters.select { |f| f.is_a?(Array) }.map { |v| GoodData::SmallGoodZilla.create_category_filter(v, project) }
vars + category
end
def create_part(stuff)
stuff = Array(stuff)
parts = stuff.reduce([]) do |memo, item|
if item.respond_to?(:metric?) && item.metric?
memo
else
memo << create_attribute_part(item)
end
memo
end
if stuff.any? { |item| item.respond_to?(:metric?) && item.metric? }
parts << 'metricGroup'
end
parts
end
def find(stuff, opts = { :client => GoodData.connection, :project => GoodData.project })
_client, project = GoodData.get_client_and_project(opts)
stuff.map do |item|
obj = if item.is_a?(String)
begin
project.objects(item)
rescue RestClient::ResourceNotFound
raise "Object given by id \"#{item}\" could not be found"
end
elsif item.is_a?(Hash) && item.keys.include?(:title)
case item[:type].to_s
when 'metric'
GoodData::Metric.find_first_by_title(item[:title], opts)
when 'attribute'
GoodData::Attribute.find_first_by_title(item[:title], opts)
end
elsif item.is_a?(Hash) && (item.keys.include?(:id) || item.keys.include?(:identifier))
id = item[:id] || item[:identifier]
case item[:type].to_s
when 'metric'
project.metrics(id)
when 'attribute'
project.attributes(id)
when 'label'
projects.labels(id)
end
else
item
end
if obj.respond_to?(:attribute?) && obj.attribute?
obj.display_forms.first
else
obj
end
end
end
def execute(options = {})
left = Array(options[:left])
top = Array(options[:top])
metrics = (left + top).select { |item| item.respond_to?(:metric?) && item.metric? }
unsaved_metrics = metrics.reject(&:saved?)
unsaved_metrics.each { |m| m.title = 'Untitled metric' unless m.title }
begin
unsaved_metrics.each(&:save)
GoodData::ReportDefinition.create(options).execute
ensure
unsaved_metrics.each { |m| m.delete if m && m.saved? }
end
end
def create(options = { :client => GoodData.connection, :project => GoodData.project })
client, project = GoodData.get_client_and_project(options)
left = Array(options[:left])
top = Array(options[:top])
filters = options[:filters] || []
left = ReportDefinition.find(left, options)
top = ReportDefinition.find(top, options)
# TODO: Put somewhere for i18n
fail_msg = 'All metrics in report definition must be saved'
fail fail_msg unless (left + top).all?(&:saved?)
pars = {
'reportDefinition' => {
'content' => {
'grid' => {
'sort' => {
'columns' => [],
'rows' => []
},
'columnWidths' => [],
'columns' => ReportDefinition.create_part(top),
'metrics' => ReportDefinition.create_metrics_part(left, top),
'rows' => ReportDefinition.create_part(left)
},
'format' => 'grid',
'filters' => ReportDefinition.create_filters_part(filters, :project => project)
},
'meta' => {
'tags' => '',
'summary' => '',
'title' => 'Untitled report definition'
}
}
}
# TODO: write test for report definitions with explicit identifiers
pars['reportDefinition']['meta']['identifier'] = options[:identifier] if options[:identifier]
client.create(ReportDefinition, pars, :project => project)
end
end
def attribute_parts
cols = content['grid']['columns'] || []
rows = content['grid']['rows'] || []
items = cols + rows
items.select { |item| item.is_a?(Hash) && item.keys.first == 'attribute' }
end
def attributes
labels.map(&:attribute)
end
# Removes the color mapping from report definition
#
# @return [GoodData::ReportDefinition] Returns self
def reset_color_mapping!
global_chart_options = GoodData::Helpers.get_path(content, %w(chart styles global))
global_chart_options['colorMapping'] = [] if global_chart_options
self
end
# Return true if the report definition is a chart
#
# @return [Boolean] Return true if report definition is a chart
def chart?
!table?
end
def labels
attribute_parts.map { |part| project.labels(part['attribute']['uri']) }
end
def metric_parts
content['grid']['metrics']
end
alias_method :measure_parts, :metric_parts
def metrics
metric_parts.map { |i| project.metrics(i['uri']) }
end
def execute(opts = {})
result = if saved?
pars = {
'report_req' => { 'reportDefinition' => uri }
}
client.post "/gdc/projects/#{project.pid}/execute", pars
else
data = {
report_req: {
definitionContent: {
content: to_hash,
projectMetadata: project.links['metadata']
}
}
}
uri = "/gdc/projects/#{project.pid}/execute"
client.post(uri, data)
end
GoodData::Report.data_result(result, opts.merge(client: client))
end
def filters
content['filters'].map { |f| f['expression'] }
end
# Method used for replacing values in their state according to mapping.
# Can be used to replace any values but it is typically used to replace
# the URIs. Returns a new object of the same type.
#
# @param [Array<Array>]Mapping specifying what should be exchanged for what. As mapping should be used output of GoodData::Helpers.prepare_mapping.
# @return [GoodData::ReportDefinition]
def replace(mapping)
x = GoodData::MdObject.replace_quoted(self, mapping)
x = GoodData::MdObject.replace_bracketed(x, mapping)
vals = GoodData::MdObject.find_replaceable_values(self, mapping)
x = GoodData::MdObject.replace_quoted(x, vals)
GoodData::MdObject.replace_bracketed(x, vals)
end
# Return true if the report definition is a table
#
# @return [Boolean] Return true if report definition is a table
def table?
content['format'] == 'grid'
end
end
end