Skip to content

Commit

Permalink
Merge pull request randym#148 from alexrothenberg/pivot_table
Browse files Browse the repository at this point in the history
Create a simple Pivot Table
  • Loading branch information
randym committed Nov 27, 2012
2 parents 7d3d588 + 036f588 commit c3a3673
Show file tree
Hide file tree
Showing 16 changed files with 637 additions and 14 deletions.
39 changes: 39 additions & 0 deletions examples/pivot_table.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env ruby -w -s
# -*- coding: utf-8 -*-

$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
require 'axlsx'

p = Axlsx::Package.new
wb = p.workbook

# Create some data in a sheet
def month
%w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec).sample
end
def year
%w(2010 2011 2012).sample
end
def type
%w(Meat Dairy Beverages Produce).sample
end
def sales
rand(5000)
end
def region
%w(East West North South).sample
end

wb.add_worksheet(:name => "Data Sheet") do |sheet|
sheet.add_row ['Month', 'Year', 'Type', 'Sales', 'Region']
30.times { sheet.add_row [month, year, type, sales, region] }
sheet.add_pivot_table 'G4:L17', "A1:E31" do |pivot_table|
pivot_table.rows = ['Month', 'Year']
pivot_table.columns = ['Type']
pivot_table.data = ['Sales']
pivot_table.pages = ['Region']
end
end

# Write the excel file
p.serialize("pivot_table.xlsx")
12 changes: 12 additions & 0 deletions lib/axlsx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ def self.cell_r(c_index, r_index)
Axlsx::col_ref(c_index).to_s << (r_index+1).to_s
end

def self.range_to_a(range)
range.match(/^(\w+?\d+)\:(\w+?\d+)$/)
start_col, start_row = name_to_indices($1)
end_col, end_row = name_to_indices($2)
(start_row..end_row).to_a.map do |row_num|
(start_col..end_col).to_a.map do |col_num|
"#{col_ref(col_num)}#{row_num+1}"
end
end
end


# performs the increadible feat of changing snake_case to CamelCase
# @param [String] s The snake case string to camelize
# @return [String]
Expand Down
13 changes: 13 additions & 0 deletions lib/axlsx/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ def parts
workbook.tables.each do |table|
parts << {:entry => "xl/#{table.pn}", :doc => table.to_xml_string, :schema => SML_XSD}
end
workbook.pivot_tables.each do |pivot_table|
cache_definition = pivot_table.cache_definition
parts << {:entry => "xl/#{pivot_table.rels_pn}", :doc => pivot_table.relationships.to_xml_string, :schema => RELS_XSD}
parts << {:entry => "xl/#{pivot_table.pn}", :doc => pivot_table.to_xml_string} #, :schema => SML_XSD}
parts << {:entry => "xl/#{cache_definition.pn}", :doc => cache_definition.to_xml_string} #, :schema => SML_XSD}
end

workbook.comments.each do|comment|
if comment.size > 0
Expand Down Expand Up @@ -255,6 +261,13 @@ def content_types
:ContentType => TABLE_CT)
end

workbook.pivot_tables.each do |pivot_table|
c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.pn}",
:ContentType => PIVOT_TABLE_CT)
c_types << Axlsx::Override.new(:PartName => "/xl/#{pivot_table.cache_definition.pn}",
:ContentType => PIVOT_TABLE_CACHE_DEFINITION_CT)
end

workbook.comments.each do |comment|
if comment.size > 0
c_types << Axlsx::Override.new(:PartName => "/xl/#{comment.pn}",
Expand Down
1 change: 1 addition & 0 deletions lib/axlsx/rels/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Relationship
# @note Supported types are defined as constants in Axlsx:
# @see XML_NS_R
# @see TABLE_R
# @see PIVOT_TABLE_R
# @see WORKBOOK_R
# @see WORKSHEET_R
# @see APP_R
Expand Down
21 changes: 19 additions & 2 deletions lib/axlsx/util/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ module Axlsx
# table rels namespace
TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"

# pivot table rels namespace
PIVOT_TABLE_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
PIVOT_TABLE_CACHE_DEFINITION_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition"

# workbook rels namespace
WORKBOOK_R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"

Expand Down Expand Up @@ -99,6 +103,12 @@ module Axlsx
# table content type
TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"

# pivot table content type
PIVOT_TABLE_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"

# pivot table cache definition content type
PIVOT_TABLE_CACHE_DEFINITION_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"

# workbook content type
WORKBOOK_CT = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"

Expand Down Expand Up @@ -208,6 +218,13 @@ module Axlsx
# drawing part
TABLE_PN = "tables/table%d.xml"

# pivot table parts
PIVOT_TABLE_PN = "pivotTables/pivotTable%d.xml"
PIVOT_TABLE_CACHE_DEFINITION_PN = "pivotCache/pivotCacheDefinition%d.xml"

# pivot table rels parts
PIVOT_TABLE_RELS_PN = "pivotTables/_rels/pivotTable%d.xml.rels"

# chart part
CHART_PN = "charts/chart%d.xml"

Expand Down Expand Up @@ -320,7 +337,7 @@ module Axlsx
# x1E Information Separator Two
# x1F Information Separator One
#
# The following are not dealt with.
# The following are not dealt with.
# If you have this in your data, expect excel to blow up!
#
# x7F Delete
Expand Down Expand Up @@ -365,7 +382,7 @@ module Axlsx
# @see http://www.codetable.net/asciikeycodes
pattern = "[\x0-\x08\x0B\x0C\x0E-\x1F]"
pattern= pattern.respond_to?(:encode) ? pattern.encode('UTF-8') : pattern

# The regular expression used to remove control characters from worksheets
CONTROL_CHAR_REGEX = Regexp.new(pattern, 'n')

Expand Down
5 changes: 3 additions & 2 deletions lib/axlsx/util/validators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def self.validate(name, regex, v)
raise ArgumentError, (ERR_REGEX % [v.inspect, regex.to_s]) unless (v.respond_to?(:to_s) && v.to_s.match(regex))
end
end

# Validate that the class of the value provided is either an instance or the class of the allowed types and that any specified additional validation returns true.
class DataTypeValidator
# Perform validation
Expand Down Expand Up @@ -229,14 +230,14 @@ def self.validate_vertical_alignment(v)
# TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, DRAWING_CT, COMMENT_CT are allowed
# @param [Any] v The value validated
def self.validate_content_type(v)
RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT], v
RestrictionValidator.validate :content_type, [TABLE_CT, WORKBOOK_CT, APP_CT, RELS_CT, STYLES_CT, XML_CT, WORKSHEET_CT, SHARED_STRINGS_CT, CORE_CT, CHART_CT, JPEG_CT, GIF_CT, PNG_CT, DRAWING_CT, COMMENT_CT, VML_DRAWING_CT, PIVOT_TABLE_CT, PIVOT_TABLE_CACHE_DEFINITION_CT], v
end

# Requires that the value is a valid relationship_type
# XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R are allowed
# @param [Any] v The value validated
def self.validate_relationship_type(v)
RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL], v
RestrictionValidator.validate :relationship_type, [XML_NS_R, TABLE_R, WORKBOOK_R, WORKSHEET_R, APP_R, RELS_R, CORE_R, STYLES_R, CHART_R, DRAWING_R, IMAGE_R, HYPERLINK_R, SHARED_STRINGS_R, COMMENT_R, VML_DRAWING_R, COMMENT_R_NULL, PIVOT_TABLE_R, PIVOT_TABLE_CACHE_DEFINITION_R], v
end

# Requires that the value is a valid table element type
Expand Down
28 changes: 25 additions & 3 deletions lib/axlsx/workbook/workbook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ module Axlsx
require 'axlsx/workbook/worksheet/table_style_info.rb'
require 'axlsx/workbook/worksheet/table.rb'
require 'axlsx/workbook/worksheet/tables.rb'
require 'axlsx/workbook/worksheet/pivot_table_cache_definition.rb'
require 'axlsx/workbook/worksheet/pivot_table.rb'
require 'axlsx/workbook/worksheet/pivot_tables.rb'
require 'axlsx/workbook/worksheet/data_validation.rb'
require 'axlsx/workbook/worksheet/data_validations.rb'
require 'axlsx/workbook/worksheet/sheet_view.rb'
Expand Down Expand Up @@ -121,10 +124,17 @@ def use_shared_strings=(v)
# @return [SimpleTypedList]
attr_reader :tables

# A colllection of pivot tables associated with this workbook
# @note The recommended way to manage drawings is Worksheet#add_table
# @see Worksheet#add_table
# @see Table
# @return [SimpleTypedList]
attr_reader :pivot_tables


# A collection of defined names for this workbook
# @note The recommended way to manage defined names is Workbook#add_defined_name
# @see DefinedName
# @see DefinedName
# @return [DefinedNames]
def defined_names
@defined_names ||= DefinedNames.new
Expand Down Expand Up @@ -170,7 +180,7 @@ def sheet_by_name(name)
# w.parse_string :date1904, "//xmlns:workbookPr/@date1904"
# w
#end

# Creates a new Workbook
# The recomended way to work with workbooks is via Package#workbook
# @option options [Boolean] date1904. If this is not specified, date1904 is set to false. Office 2011 for Mac defaults to false.
Expand All @@ -182,6 +192,7 @@ def initialize(options={})
@images = SimpleTypedList.new Pic
# Are these even used????? Check package serialization parts
@tables = SimpleTypedList.new Table
@pivot_tables = SimpleTypedList.new PivotTable
@comments = SimpleTypedList.new Comments


Expand Down Expand Up @@ -217,7 +228,7 @@ def use_autowidth() @use_autowidth; end
def use_autowidth=(v=true) Axlsx::validate_boolean v; @use_autowidth = v; end

# inserts a worksheet into this workbook at the position specified.
# It the index specified is out of range, the worksheet will be added to the end of the
# It the index specified is out of range, the worksheet will be added to the end of the
# worksheets collection
# @return [Worksheet]
# @param index The zero based position to insert the newly created worksheet
Expand Down Expand Up @@ -259,6 +270,9 @@ def relationships
@worksheets.each do |sheet|
r << Relationship.new(WORKSHEET_R, WORKSHEET_PN % (r.size+1))
end
pivot_tables.each_with_index do |pivot_table, index|
r << Relationship.new(PIVOT_TABLE_CACHE_DEFINITION_R, PIVOT_TABLE_CACHE_DEFINITION_PN % (index+1))
end
r << Relationship.new(STYLES_R, STYLES_PN)
if use_shared_strings
r << Relationship.new(SHARED_STRINGS_R, SHARED_STRINGS_PN)
Expand Down Expand Up @@ -299,6 +313,14 @@ def to_xml_string(str='')
end
end
str << '</sheets>'
unless pivot_tables.empty?
str << '<pivotCaches>'
pivot_tables.each_with_index do |pivot_table, index|
rId = "rId#{@worksheets.size + index + 1 }"
str << '<pivotCache cacheId="' << pivot_table.cache_definition.cache_id.to_s << '" r:id="' << rId << '"/>'
end
str << '</pivotCaches>'
end
defined_names.to_xml_string(str)
str << '</workbook>'
end
Expand Down
Loading

0 comments on commit c3a3673

Please sign in to comment.