diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/axlsx/util/validators.rb | 29 | ||||
| -rw-r--r-- | lib/axlsx/workbook/workbook.rb | 2 | ||||
| -rw-r--r-- | lib/axlsx/workbook/worksheet/conditional_formatting.rb | 69 | ||||
| -rw-r--r-- | lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb | 169 | ||||
| -rw-r--r-- | lib/axlsx/workbook/worksheet/worksheet.rb | 21 |
5 files changed, 288 insertions, 2 deletions
diff --git a/lib/axlsx/util/validators.rb b/lib/axlsx/util/validators.rb index 54a0aeb4..14d34d36 100644 --- a/lib/axlsx/util/validators.rb +++ b/lib/axlsx/util/validators.rb @@ -96,6 +96,35 @@ module Axlsx :darkTrellis, :lightHorizontal, :lightVertical, :lightDown, :lightUp, :lightGrid, :lightTrellis, :gray125, :gray0625], v end + # Requires that the value is one of the ST_TimePeriod types + # valid time period types are today, yesterday, tomorrow, last7Days, + # thisMonth, lastMonth, nextMonth, thisWeek, lastWeek, nextWeek + def self.validate_time_period_type(v) + RestrictionValidator.validate :time_period_type, [:today, :yesterday, :tomorrow, :last7Days, :thisMonth, :lastMonth, :nextMonth, :thisWeek, :lastWeek, :nextWeek], v + + + end + + # Requires that the value is valid conditional formatting type. + # valid types must be one of expression, cellIs, colorScale, + # dataBar, iconSet, top10, uniqueValues, duplicateValues, + # containsText, notContainsText, beginsWith, endsWith, + # containsBlanks, notContainsBlanks, containsErrors, + # notContainsErrors, timePeriod, aboveAverage + # @param [Any] v The value validated + def self.validate_conditional_formatting_type(v) + RestrictionValidator.validate :conditional_formatting_type, [:expression, :cellIs, :colorScale, :dataBar, :iconSet, :top10, :uniqueValues, :duplicateValues, :containsText, :notContainsText, :beginsWith, :endsWith, :containsBlanks, :notContainsBlanks, :containsErrors, :notContainsErrors, :timePeriod, :aboveAverage], v + end + + # Requires that the value is valid conditional formatting operator. + # valid operators must be one of lessThan, lessThanOrEqual, equal, + # notEqual, greaterThanOrEqual, greaterThan, between, notBetween, + # containsText, notContains, beginsWith, endsWith + # @param [Any] v The value validated + def self.validate_conditional_formatting_operator(v) + RestrictionValidator.validate :conditional_formatting_type, [:lessThan, :lessThanOrEqual, :equal, :notEqual, :greaterThanOrEqual, :greaterThan, :between, :notBetween, :containsText, :notContains, :beginsWith, :endsWith], v + end + # Requires that the value is a gradient_type. # valid types are :linear and :path # @param [Any] v The value validated diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb index ce4e161b..2d406890 100644 --- a/lib/axlsx/workbook/workbook.rb +++ b/lib/axlsx/workbook/workbook.rb @@ -4,6 +4,8 @@ module Axlsx require 'axlsx/workbook/worksheet/date_time_converter.rb' require 'axlsx/workbook/worksheet/cell.rb' require 'axlsx/workbook/worksheet/page_margins.rb' +require 'axlsx/workbook/worksheet/conditional_formatting.rb' +require 'axlsx/workbook/worksheet/conditional_formatting_rule.rb' require 'axlsx/workbook/worksheet/row.rb' require 'axlsx/workbook/worksheet/col.rb' require 'axlsx/workbook/worksheet/worksheet.rb' diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting.rb b/lib/axlsx/workbook/worksheet/conditional_formatting.rb new file mode 100644 index 00000000..01a1d719 --- /dev/null +++ b/lib/axlsx/workbook/worksheet/conditional_formatting.rb @@ -0,0 +1,69 @@ +module Axlsx + # Conditional formatting allows styling of ranges based on functions + # + # @note The recommended way to manage conditional formatting is via Worksheet#add_conditional_formatting + # @see Worksheet#add_conditional_formatting + class ConditionalFormatting + + # Sqref + # Range over which the formatting is applied + # @return [String] + attr_reader :sqref + + # Rules + # Rules to apply the formatting to. Can be either a hash of + # options for one ConditionalFormattingRule, an array of hashes + # for multiple ConditionalFormattingRules, or an array of already + # created ConditionalFormattingRules. + # @return [Array] + attr_reader :rules + + # Creates a new ConditionalFormatting object + # @option options [Array] rules The rules to apply + # @option options [String] sqref The range to apply the rules to + def initialize(options={}) + @rules = [] + options.each do |o| + self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}=" + end + end + + # Add ConditionalFormattingRules to this object. Rules can either + # be already created objects or hashes of options for automatic + # creation. + # @option rules [Array, Hash] the rules apply, can be just one in hash form + # @see ConditionalFormattingRule#initialize + def add_rules(rules) + rules = [rules] if rules.is_a? Hash + conditional_rules = rules.each do |rule| + add_rule rule + end + end + + # Add a ConditionalFormattingRule. If a hash of options is passed + # in create a rule on the fly + # @option rule [ConditionalFormattingRule, Hash] A rule to create + # @see ConditionalFormattingRule#initialize + def add_rule(rule) + if rule.is_a? Axlsx::ConditionalFormattingRule + @rules << rule + elsif rule.is_a? Hash + @rules << ConditionalFormattingRule.new(rule) + end + end + + # @see rules + def rules=(v); @rules = v end + # @see sqref + def sqref=(v); Axlsx::validate_string(v); @sqref = v end + + # Serializes the conditional formatting element + # @param [String] str + # @return [String] + def to_xml_string(str = '') + str << '<conditionalFormatting sqref="' << sqref << '">' + str << rules.collect{ |rule| rule.to_xml_string }.join(' ') + str << '</conditionalFormatting>' + end + end +end diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb new file mode 100644 index 00000000..bddea5a5 --- /dev/null +++ b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb @@ -0,0 +1,169 @@ +module Axlsx + # Conditional formatting rules specify formulas whose evaluations + # format cells + # + # @note The recommended way to manage these rules is via Worksheet#add_conditional_formatting + # @see Worksheet#add_conditional_formatting + class ConditionalFormattingRule + @@child_elements = [:formula] + + # Formula + # @return [String] + attr_reader :formula + + # Type (ST_CfType) + # options are expression, cellIs, colorScale, dataBar, iconSet, + # top10, uniqueValues, duplicateValues, containsText, + # notContainsText, beginsWith, endsWith, containsBlanks, + # notContainsBlanks, containsErrors, notContainsErrors, + # timePeriod, aboveAverage + # @return [Symbol] + attr_reader :type + + # Above average rule + # Indicates whether the rule is an "above average" rule. True + # indicates 'above average'. This attribute is ignored if type is + # not equal to aboveAverage. + # @return [Boolean] + attr_reader :aboveAverage + + # Bottom N rule + # @return [Boolean] + attr_reader :bottom + + # Differential Formatting Id + # @return [Integer] + attr_reader :dxfId + + # Equal Average + # Flag indicating whether the 'aboveAverage' and 'belowAverage' + # criteria is inclusive of the average itself, or exclusive of + # that value. + # @return [Boolean] + attr_reader :equalAverage + + # Operator + # The operator in a "cell value is" conditional formatting + # rule. This attribute is ignored if type is not equal to cellIs + # + # Operator must be one of lessThan, lessThanOrEqual, equal, + # notEqual, greaterThanOrEqual, greaterThan, between, notBetween, + # containsText, notContains, beginsWith, endsWith + # @return [Symbol] + attr_reader :operator + + # Priority + # The priority of this conditional formatting rule. This value is + # used to determine which format should be evaluated and + # rendered. Lower numeric values are higher priority than higher + # numeric values, where '1' is the highest priority. + # @return [Integer] + attr_reader :priority + + # Text + # The text value in a "text contains" conditional formatting + # rule. + # @return [String] + attr_reader :text + + # percent (Top 10 Percent) + # Indicates whether a "top/bottom n" rule is a "top/bottom n + # percent" rule. This attribute is ignored if type is not equal to + # top10. + # @return [Boolean] + attr_reader :percent + + # rank (Rank) + # The value of "n" in a "top/bottom n" conditional formatting + # rule. This attribute is ignored if type is not equal to top10. + # @return [Integer] + attr_reader :rank + + # stdDev (StdDev) + # The number of standard deviations to include above or below the + # average in the conditional formatting rule. This attribute is + # ignored if type is not equal to aboveAverage. If a value is + # present for stdDev and the rule type = aboveAverage, then this + # rule is automatically an "above or below N standard deviations" + # rule. + # @return [Integer] + attr_reader :stdDev + + # stopIfTrue (Stop If True) + # If this flag is '1', no rules with lower priority shall be + # applied over this rule, when this rule evaluates to true. + # @return [Boolean] + attr_reader :stopIfTrue + + # timePeriod (Time Period) + # The applicable time period in a "date occurring…" conditional + # formatting rule. This attribute is ignored if type is not equal + # to timePeriod. + # Valid types are today, yesterday, tomorrow, last7Days, + # thisMonth, lastMonth, nextMonth, thisWeek, lastWeek, nextWeek + attr_reader :timePeriod + + # Creates a new Conditional Formatting Rule object + # @option options [Symbol] type The type of this formatting rule + # @option options [Boolean] aboveAverage This is an aboveAverage rule + # @option options [Boolean] bottom This is a bottom N rule. + # @option options [Integer] dxfId The formatting id to apply to matches + # @option options [Boolean] equalAverage Is the aboveAverage or belowAverage rule inclusive + # @option options [Integer] priority The priority of the rule, 1 is highest + # @option options [Symbol] operator Which operator to apply + # @option options [String] text The value to apply a text operator against + # @option options [Boolean] percent If a top/bottom N rule, evaluate as N% rather than N + # @option options [Integer] rank If a top/bottom N rule, the value of N + # @option options [Integer] stdDev The number of standard deviations above or below the average to match + # @option options [Boolean] stopIfTrue Stop evaluating rules after this rule matches + # @option options [Symbol] timePeriod The time period in a date occuring... rule + # @option options [String] formula The formula to match against in i.e. an equal rule + def initialize(options={}) + options.each do |o| + self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}=" + end + end + + # @see type + def type=(v); Axlsx::validate_conditional_formatting_type(v); @type = v end + # @see aboveAverage + def aboveAverage=(v); Axlsx::validate_boolean(v); @aboveAverage = v end + # @see bottom + def bottom=(v); Axlsx::validate_boolean(v); @bottom = v end + # @see dxfId + def dxfId=(v); Axlsx::validate_unsigned_numeric(v); @dxfId = v end + # @see equalAverage + def equalAverage=(v); Axlsx::validate_boolean(v); @equalAverage = v end + # @see priority + def priority=(v); Axlsx::validate_unsigned_numeric(v); @priority = v end + # @see operator + def operator=(v); Axlsx::validate_conditional_formatting_operator(v); @operator = v end + # @see text + def text=(v); Axlsx::validate_string(v); @text = v end + # @see percent + def percent=(v); Axlsx::validate_boolean(v); @percent = v end + # @see rank + def rank=(v); Axlsx::validate_unsigned_numeric(v); @rank = v end + # @see stdDev + def stdDev=(v); Axlsx::validate_unsigned_numeric(v); @stdDev = v end + # @see stopIfTrue + def stopIfTrue=(v); Axlsx::validate_boolean(v); @stopIfTrue = v end + # @see timePeriod + def timePeriod=(v); Axlsx::validate_time_period_type(v); @timePeriod = v end + # @see formula + def formula=(v); Axlsx::validate_string(v); @formula = v end + + # Serializes the conditional formatting rule + # @param [String] str + # @return [String] + def to_xml_string(str = '') + str << '<cfRule ' + str << instance_values.map { |key, value| '' << key << '="' << value.to_s << '"' unless @@child_elements.include?(key.to_sym) }.join(' ') + str << '>' + @@child_elements.each do |el| + str << "<#{el}>" << self.send(el) << "</#{el}>" if self.send(el) + end + str << '</cfRule>' + end + end +end diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb index 6433b9bc..05b13195 100644 --- a/lib/axlsx/workbook/worksheet/worksheet.rb +++ b/lib/axlsx/workbook/worksheet/worksheet.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 module Axlsx # The Worksheet class represents a worksheet in the workbook. @@ -101,7 +100,8 @@ module Axlsx @drawing = @page_margins = @auto_filter = nil @merged_cells = [] @auto_fit_data = [] - + @conditional_formattings = [] + @selected = false @show_gridlines = true self.name = "Sheet" + (index+1).to_s @@ -123,6 +123,20 @@ module Axlsx rows.flatten end + # Add conditinoal formatting to this worksheet. + # + # @option cells [String] The range to apply the formatting to + # @option rules [Array, Hash] An array of hashes (or just one) to create Conditional formatting rules from + # @example This would color column A whenever it is FALSE + # worksheet.add_conditional_formatting( "A1:A1048576", { :type => :cellIs, :operator => :equal, :formula => "FALSE", :dxfId => 0, priority => 1 } + # + # @see ConditionalFormattingRule.#initialize + def add_conditional_formatting(cells, rules) + cf = ConditionalFormatting.new( :sqref => cells ) + cf.add_rules rules + @conditional_formattings << cf + end + # Creates merge information for this worksheet. # Cells can be merged by calling the merge_cells method on a worksheet. # @example This would merge the three cells C1..E1 # @@ -411,6 +425,9 @@ module Axlsx unless @tables.empty? str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }] end + @conditional_formattings.each do |cf| + str.concat cf.to_xml_string + end str + '</worksheet>' end |
